blob: 562042620e339b6af78cc30bea3c73666e2fc4ed [file] [log] [blame]
Alan Donovan312d1a52017-10-02 10:10:28 -04001// Copyright 2017 The Bazel Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Alan Donovane3deafe2018-10-23 11:05:09 -04005package starlark
Alan Donovan312d1a52017-10-02 10:10:28 -04006
7// This file defines the library of built-ins.
8//
9// Built-ins must explicitly check the "frozen" flag before updating
10// mutable types such as lists and dicts.
11
12import (
alandonovan30ae18b2019-05-28 16:29:25 -040013 "errors"
Alan Donovan312d1a52017-10-02 10:10:28 -040014 "fmt"
alandonovan3b7e02e2020-11-11 12:03:41 -050015 "math"
Alan Donovan312d1a52017-10-02 10:10:28 -040016 "math/big"
17 "os"
18 "sort"
19 "strconv"
20 "strings"
21 "unicode"
alandonovane35f71a2019-05-14 14:50:48 -040022 "unicode/utf16"
Alan Donovan312d1a52017-10-02 10:10:28 -040023 "unicode/utf8"
24
Alan Donovan6beab7e2018-10-31 17:53:09 -040025 "go.starlark.net/syntax"
Alan Donovan312d1a52017-10-02 10:10:28 -040026)
27
28// Universe defines the set of universal built-ins, such as None, True, and len.
29//
30// The Go application may add or remove items from the
Alan Donovane3deafe2018-10-23 11:05:09 -040031// universe dictionary before Starlark evaluation begins.
Alan Donovan312d1a52017-10-02 10:10:28 -040032// All values in the dictionary must be immutable.
Alan Donovane3deafe2018-10-23 11:05:09 -040033// Starlark programs cannot modify the dictionary.
Alan Donovan312d1a52017-10-02 10:10:28 -040034var Universe StringDict
35
36func init() {
alandonovan04aba6e2018-11-05 17:45:33 -050037 // https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-constants-and-functions
Alan Donovan312d1a52017-10-02 10:10:28 -040038 Universe = StringDict{
39 "None": None,
40 "True": True,
41 "False": False,
42 "any": NewBuiltin("any", any),
43 "all": NewBuiltin("all", all),
44 "bool": NewBuiltin("bool", bool_),
alandonovanebe61bd2021-02-12 16:57:32 -050045 "bytes": NewBuiltin("bytes", bytes_),
Alan Donovan312d1a52017-10-02 10:10:28 -040046 "chr": NewBuiltin("chr", chr),
Alan Donovan312d1a52017-10-02 10:10:28 -040047 "dict": NewBuiltin("dict", dict),
48 "dir": NewBuiltin("dir", dir),
49 "enumerate": NewBuiltin("enumerate", enumerate),
alandonovan30ae18b2019-05-28 16:29:25 -040050 "fail": NewBuiltin("fail", fail),
alandonovan3b7e02e2020-11-11 12:03:41 -050051 "float": NewBuiltin("float", float),
Alan Donovan312d1a52017-10-02 10:10:28 -040052 "getattr": NewBuiltin("getattr", getattr),
53 "hasattr": NewBuiltin("hasattr", hasattr),
54 "hash": NewBuiltin("hash", hash),
55 "int": NewBuiltin("int", int_),
56 "len": NewBuiltin("len", len_),
57 "list": NewBuiltin("list", list),
58 "max": NewBuiltin("max", minmax),
59 "min": NewBuiltin("min", minmax),
60 "ord": NewBuiltin("ord", ord),
61 "print": NewBuiltin("print", print),
62 "range": NewBuiltin("range", range_),
63 "repr": NewBuiltin("repr", repr),
64 "reversed": NewBuiltin("reversed", reversed),
65 "set": NewBuiltin("set", set), // requires resolve.AllowSet
66 "sorted": NewBuiltin("sorted", sorted),
67 "str": NewBuiltin("str", str),
68 "tuple": NewBuiltin("tuple", tuple),
69 "type": NewBuiltin("type", type_),
70 "zip": NewBuiltin("zip", zip),
71 }
72}
73
Alan Donovan312d1a52017-10-02 10:10:28 -040074// methods of built-in types
alandonovan04aba6e2018-11-05 17:45:33 -050075// https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-methods
Alan Donovan312d1a52017-10-02 10:10:28 -040076var (
alandonovanebe61bd2021-02-12 16:57:32 -050077 bytesMethods = map[string]*Builtin{
78 "elems": NewBuiltin("elems", bytes_elems),
79 }
80
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +000081 dictMethods = map[string]*Builtin{
82 "clear": NewBuiltin("clear", dict_clear),
83 "get": NewBuiltin("get", dict_get),
84 "items": NewBuiltin("items", dict_items),
85 "keys": NewBuiltin("keys", dict_keys),
86 "pop": NewBuiltin("pop", dict_pop),
87 "popitem": NewBuiltin("popitem", dict_popitem),
88 "setdefault": NewBuiltin("setdefault", dict_setdefault),
89 "update": NewBuiltin("update", dict_update),
90 "values": NewBuiltin("values", dict_values),
Alan Donovan312d1a52017-10-02 10:10:28 -040091 }
92
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +000093 listMethods = map[string]*Builtin{
94 "append": NewBuiltin("append", list_append),
95 "clear": NewBuiltin("clear", list_clear),
96 "extend": NewBuiltin("extend", list_extend),
97 "index": NewBuiltin("index", list_index),
98 "insert": NewBuiltin("insert", list_insert),
99 "pop": NewBuiltin("pop", list_pop),
100 "remove": NewBuiltin("remove", list_remove),
Alan Donovan312d1a52017-10-02 10:10:28 -0400101 }
102
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +0000103 stringMethods = map[string]*Builtin{
104 "capitalize": NewBuiltin("capitalize", string_capitalize),
105 "codepoint_ords": NewBuiltin("codepoint_ords", string_iterable),
106 "codepoints": NewBuiltin("codepoints", string_iterable), // sic
107 "count": NewBuiltin("count", string_count),
108 "elem_ords": NewBuiltin("elem_ords", string_iterable),
109 "elems": NewBuiltin("elems", string_iterable), // sic
110 "endswith": NewBuiltin("endswith", string_startswith), // sic
111 "find": NewBuiltin("find", string_find),
112 "format": NewBuiltin("format", string_format),
113 "index": NewBuiltin("index", string_index),
114 "isalnum": NewBuiltin("isalnum", string_isalnum),
115 "isalpha": NewBuiltin("isalpha", string_isalpha),
116 "isdigit": NewBuiltin("isdigit", string_isdigit),
117 "islower": NewBuiltin("islower", string_islower),
118 "isspace": NewBuiltin("isspace", string_isspace),
119 "istitle": NewBuiltin("istitle", string_istitle),
120 "isupper": NewBuiltin("isupper", string_isupper),
121 "join": NewBuiltin("join", string_join),
122 "lower": NewBuiltin("lower", string_lower),
123 "lstrip": NewBuiltin("lstrip", string_strip), // sic
124 "partition": NewBuiltin("partition", string_partition),
125 "replace": NewBuiltin("replace", string_replace),
126 "rfind": NewBuiltin("rfind", string_rfind),
127 "rindex": NewBuiltin("rindex", string_rindex),
128 "rpartition": NewBuiltin("rpartition", string_partition), // sic
129 "rsplit": NewBuiltin("rsplit", string_split), // sic
130 "rstrip": NewBuiltin("rstrip", string_strip), // sic
131 "split": NewBuiltin("split", string_split),
132 "splitlines": NewBuiltin("splitlines", string_splitlines),
133 "startswith": NewBuiltin("startswith", string_startswith),
134 "strip": NewBuiltin("strip", string_strip),
135 "title": NewBuiltin("title", string_title),
136 "upper": NewBuiltin("upper", string_upper),
Alan Donovan312d1a52017-10-02 10:10:28 -0400137 }
138
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +0000139 setMethods = map[string]*Builtin{
140 "union": NewBuiltin("union", set_union),
Alan Donovan312d1a52017-10-02 10:10:28 -0400141 }
142)
143
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +0000144func builtinAttr(recv Value, name string, methods map[string]*Builtin) (Value, error) {
145 b := methods[name]
146 if b == nil {
Alan Donovan312d1a52017-10-02 10:10:28 -0400147 return nil, nil // no such method
148 }
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +0000149 return b.BindReceiver(recv), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400150}
151
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +0000152func builtinAttrNames(methods map[string]*Builtin) []string {
Alan Donovan312d1a52017-10-02 10:10:28 -0400153 names := make([]string, 0, len(methods))
154 for name := range methods {
155 names = append(names, name)
156 }
157 sort.Strings(names)
158 return names
159}
160
alandonovan4cbd8962017-10-19 13:18:36 -0400161// ---- built-in functions ----
Alan Donovan312d1a52017-10-02 10:10:28 -0400162
alandonovan04aba6e2018-11-05 17:45:33 -0500163// https://github.com/google/starlark-go/blob/master/doc/spec.md#all
Alan Donovan312d1a52017-10-02 10:10:28 -0400164func all(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
165 var iterable Iterable
166 if err := UnpackPositionalArgs("all", args, kwargs, 1, &iterable); err != nil {
167 return nil, err
168 }
169 iter := iterable.Iterate()
170 defer iter.Done()
171 var x Value
172 for iter.Next(&x) {
173 if !x.Truth() {
174 return False, nil
175 }
176 }
177 return True, nil
178}
179
alandonovan04aba6e2018-11-05 17:45:33 -0500180// https://github.com/google/starlark-go/blob/master/doc/spec.md#any
Alan Donovan312d1a52017-10-02 10:10:28 -0400181func any(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
182 var iterable Iterable
Josh Bleecher Snyderf50f3592018-12-12 11:18:09 -0800183 if err := UnpackPositionalArgs("any", args, kwargs, 1, &iterable); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -0400184 return nil, err
185 }
186 iter := iterable.Iterate()
187 defer iter.Done()
188 var x Value
189 for iter.Next(&x) {
190 if x.Truth() {
191 return True, nil
192 }
193 }
194 return False, nil
195}
196
alandonovan04aba6e2018-11-05 17:45:33 -0500197// https://github.com/google/starlark-go/blob/master/doc/spec.md#bool
Alan Donovan312d1a52017-10-02 10:10:28 -0400198func bool_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
199 var x Value = False
200 if err := UnpackPositionalArgs("bool", args, kwargs, 0, &x); err != nil {
201 return nil, err
202 }
203 return x.Truth(), nil
204}
205
alandonovanebe61bd2021-02-12 16:57:32 -0500206// https://github.com/google/starlark-go/blob/master/doc/spec.md#bytes
207func bytes_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
208 if len(kwargs) > 0 {
209 return nil, fmt.Errorf("bytes does not accept keyword arguments")
210 }
211 if len(args) != 1 {
212 return nil, fmt.Errorf("bytes: got %d arguments, want exactly 1", len(args))
213 }
214 switch x := args[0].(type) {
215 case Bytes:
216 return x, nil
217 case String:
218 // Invalid encodings are replaced by that of U+FFFD.
219 return Bytes(utf8Transcode(string(x))), nil
220 case Iterable:
221 // iterable of numeric byte values
222 var buf strings.Builder
223 if n := Len(x); n >= 0 {
224 // common case: known length
225 buf.Grow(n)
226 }
227 iter := x.Iterate()
228 defer iter.Done()
229 var elem Value
230 var b byte
231 for i := 0; iter.Next(&elem); i++ {
232 if err := AsInt(elem, &b); err != nil {
233 return nil, fmt.Errorf("bytes: at index %d, %s", i, err)
234 }
235 buf.WriteByte(b)
236 }
237 return Bytes(buf.String()), nil
238
239 default:
240 // Unlike string(foo), which stringifies it, bytes(foo) is an error.
241 return nil, fmt.Errorf("bytes: got %s, want string, bytes, or iterable of ints", x.Type())
242 }
243}
244
alandonovan04aba6e2018-11-05 17:45:33 -0500245// https://github.com/google/starlark-go/blob/master/doc/spec.md#chr
Alan Donovan312d1a52017-10-02 10:10:28 -0400246func chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
247 if len(kwargs) > 0 {
248 return nil, fmt.Errorf("chr does not accept keyword arguments")
249 }
250 if len(args) != 1 {
251 return nil, fmt.Errorf("chr: got %d arguments, want 1", len(args))
252 }
253 i, err := AsInt32(args[0])
254 if err != nil {
tdakkota88a10932020-09-29 15:29:13 +0300255 return nil, fmt.Errorf("chr: %s", err)
Alan Donovan312d1a52017-10-02 10:10:28 -0400256 }
257 if i < 0 {
258 return nil, fmt.Errorf("chr: Unicode code point %d out of range (<0)", i)
259 }
260 if i > unicode.MaxRune {
261 return nil, fmt.Errorf("chr: Unicode code point U+%X out of range (>0x10FFFF)", i)
262 }
alandonovan3353e412020-06-11 11:19:22 -0400263 return String(string(rune(i))), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400264}
265
alandonovan04aba6e2018-11-05 17:45:33 -0500266// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict
Alan Donovan312d1a52017-10-02 10:10:28 -0400267func dict(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
268 if len(args) > 1 {
269 return nil, fmt.Errorf("dict: got %d arguments, want at most 1", len(args))
270 }
271 dict := new(Dict)
272 if err := updateDict(dict, args, kwargs); err != nil {
273 return nil, fmt.Errorf("dict: %v", err)
274 }
275 return dict, nil
276}
277
alandonovan04aba6e2018-11-05 17:45:33 -0500278// https://github.com/google/starlark-go/blob/master/doc/spec.md#dir
Alan Donovan312d1a52017-10-02 10:10:28 -0400279func dir(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
280 if len(kwargs) > 0 {
281 return nil, fmt.Errorf("dir does not accept keyword arguments")
282 }
283 if len(args) != 1 {
284 return nil, fmt.Errorf("dir: got %d arguments, want 1", len(args))
285 }
286
287 var names []string
288 if x, ok := args[0].(HasAttrs); ok {
289 names = x.AttrNames()
290 }
alandonovancb523da2019-04-01 23:09:56 -0400291 sort.Strings(names)
Alan Donovan312d1a52017-10-02 10:10:28 -0400292 elems := make([]Value, len(names))
293 for i, name := range names {
294 elems[i] = String(name)
295 }
296 return NewList(elems), nil
297}
298
alandonovan04aba6e2018-11-05 17:45:33 -0500299// https://github.com/google/starlark-go/blob/master/doc/spec.md#enumerate
Alan Donovan312d1a52017-10-02 10:10:28 -0400300func enumerate(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
301 var iterable Iterable
302 var start int
303 if err := UnpackPositionalArgs("enumerate", args, kwargs, 1, &iterable, &start); err != nil {
304 return nil, err
305 }
306
307 iter := iterable.Iterate()
Alan Donovan312d1a52017-10-02 10:10:28 -0400308 defer iter.Done()
309
310 var pairs []Value
311 var x Value
312
313 if n := Len(iterable); n >= 0 {
314 // common case: known length
315 pairs = make([]Value, 0, n)
316 array := make(Tuple, 2*n) // allocate a single backing array
317 for i := 0; iter.Next(&x); i++ {
318 pair := array[:2:2]
319 array = array[2:]
320 pair[0] = MakeInt(start + i)
321 pair[1] = x
322 pairs = append(pairs, pair)
323 }
324 } else {
325 // non-sequence (unknown length)
326 for i := 0; iter.Next(&x); i++ {
327 pair := Tuple{MakeInt(start + i), x}
328 pairs = append(pairs, pair)
329 }
330 }
331
332 return NewList(pairs), nil
333}
334
alandonovan30ae18b2019-05-28 16:29:25 -0400335// https://github.com/google/starlark-go/blob/master/doc/spec.md#fail
336func fail(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
337 sep := " "
338 if err := UnpackArgs("fail", nil, kwargs, "sep?", &sep); err != nil {
339 return nil, err
340 }
341 buf := new(strings.Builder)
342 buf.WriteString("fail: ")
343 for i, v := range args {
344 if i > 0 {
345 buf.WriteString(sep)
346 }
347 if s, ok := AsString(v); ok {
348 buf.WriteString(s)
349 } else {
350 writeValue(buf, v, nil)
351 }
352 }
353
354 return nil, errors.New(buf.String())
355}
356
alandonovan34f02112019-04-19 14:28:27 -0400357func float(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400358 if len(kwargs) > 0 {
359 return nil, fmt.Errorf("float does not accept keyword arguments")
360 }
361 if len(args) == 0 {
362 return Float(0.0), nil
363 }
364 if len(args) != 1 {
365 return nil, fmt.Errorf("float got %d arguments, wants 1", len(args))
366 }
367 switch x := args[0].(type) {
368 case Bool:
369 if x {
370 return Float(1.0), nil
371 } else {
372 return Float(0.0), nil
373 }
374 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500375 return x.finiteFloat()
Alan Donovan312d1a52017-10-02 10:10:28 -0400376 case Float:
377 return x, nil
378 case String:
alandonovan3b7e02e2020-11-11 12:03:41 -0500379 if x == "" {
380 return nil, fmt.Errorf("float: empty string")
381 }
382 // +/- NaN or Inf or Infinity (case insensitive)?
383 s := string(x)
384 switch x[len(x)-1] {
385 case 'y', 'Y':
386 if strings.EqualFold(s, "infinity") || strings.EqualFold(s, "+infinity") {
387 return inf, nil
388 } else if strings.EqualFold(s, "-infinity") {
389 return neginf, nil
390 }
391 case 'f', 'F':
392 if strings.EqualFold(s, "inf") || strings.EqualFold(s, "+inf") {
393 return inf, nil
394 } else if strings.EqualFold(s, "-inf") {
395 return neginf, nil
396 }
397 case 'n', 'N':
398 if strings.EqualFold(s, "nan") || strings.EqualFold(s, "+nan") || strings.EqualFold(s, "-nan") {
399 return nan, nil
400 }
401 }
402 f, err := strconv.ParseFloat(s, 64)
403 if math.IsInf(f, 0) {
404 return nil, fmt.Errorf("floating-point number too large")
405 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400406 if err != nil {
alandonovan3b7e02e2020-11-11 12:03:41 -0500407 return nil, fmt.Errorf("invalid float literal: %s", s)
Alan Donovan312d1a52017-10-02 10:10:28 -0400408 }
409 return Float(f), nil
410 default:
411 return nil, fmt.Errorf("float got %s, want number or string", x.Type())
412 }
413}
414
alandonovan3b7e02e2020-11-11 12:03:41 -0500415var (
416 inf = Float(math.Inf(+1))
417 neginf = Float(math.Inf(-1))
418 nan = Float(math.NaN())
419)
420
alandonovan04aba6e2018-11-05 17:45:33 -0500421// https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr
alandonovan34f02112019-04-19 14:28:27 -0400422func getattr(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400423 var object, dflt Value
424 var name string
425 if err := UnpackPositionalArgs("getattr", args, kwargs, 2, &object, &name, &dflt); err != nil {
426 return nil, err
427 }
alandonovan653c1572018-02-26 17:11:13 -0500428 if object, ok := object.(HasAttrs); ok {
429 v, err := object.Attr(name)
430 if err != nil {
431 // An error could mean the field doesn't exist,
432 // or it exists but could not be computed.
433 if dflt != nil {
434 return dflt, nil
435 }
alandonovan34f02112019-04-19 14:28:27 -0400436 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -0400437 }
alandonovan653c1572018-02-26 17:11:13 -0500438 if v != nil {
439 return v, nil
440 }
441 // (nil, nil) => no such field
Alan Donovan312d1a52017-10-02 10:10:28 -0400442 }
443 if dflt != nil {
444 return dflt, nil
445 }
alandonovanf223ef82019-03-12 16:06:40 -0400446 return nil, fmt.Errorf("getattr: %s has no .%s field or method", object.Type(), name)
Alan Donovan312d1a52017-10-02 10:10:28 -0400447}
448
alandonovan04aba6e2018-11-05 17:45:33 -0500449// https://github.com/google/starlark-go/blob/master/doc/spec.md#hasattr
Alan Donovan312d1a52017-10-02 10:10:28 -0400450func hasattr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
451 var object Value
452 var name string
453 if err := UnpackPositionalArgs("hasattr", args, kwargs, 2, &object, &name); err != nil {
454 return nil, err
455 }
456 if object, ok := object.(HasAttrs); ok {
alandonovancada8682018-02-26 12:59:19 -0500457 v, err := object.Attr(name)
458 if err == nil {
459 return Bool(v != nil), nil
460 }
461
462 // An error does not conclusively indicate presence or
463 // absence of a field: it could occur while computing
464 // the value of a present attribute, or it could be a
465 // "no such attribute" error with details.
466 for _, x := range object.AttrNames() {
467 if x == name {
468 return True, nil
469 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400470 }
471 }
472 return False, nil
473}
474
alandonovan04aba6e2018-11-05 17:45:33 -0500475// https://github.com/google/starlark-go/blob/master/doc/spec.md#hash
Alan Donovan312d1a52017-10-02 10:10:28 -0400476func hash(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovanebe61bd2021-02-12 16:57:32 -0500477 var x Value
478 if err := UnpackPositionalArgs("hash", args, kwargs, 1, &x); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -0400479 return nil, err
480 }
alandonovane35f71a2019-05-14 14:50:48 -0400481
alandonovanebe61bd2021-02-12 16:57:32 -0500482 var h int
483 switch x := x.(type) {
484 case String:
485 // The Starlark spec requires that the hash function be
486 // deterministic across all runs, motivated by the need
487 // for reproducibility of builds. Thus we cannot call
488 // String.Hash, which uses the fastest implementation
489 // available, because as varies across process restarts,
490 // and may evolve with the implementation.
491 h = int(javaStringHash(string(x)))
492 case Bytes:
493 h = int(softHashString(string(x))) // FNV32
494 default:
495 return nil, fmt.Errorf("hash: got %s, want string or bytes", x.Type())
496 }
497 return MakeInt(h), nil
alandonovane35f71a2019-05-14 14:50:48 -0400498}
499
500// javaStringHash returns the same hash as would be produced by
501// java.lang.String.hashCode. This requires transcoding the string to
502// UTF-16; transcoding may introduce Unicode replacement characters
503// U+FFFD if s does not contain valid UTF-8.
504func javaStringHash(s string) (h int32) {
505 for _, r := range s {
506 if utf16.IsSurrogate(r) {
507 c1, c2 := utf16.EncodeRune(r)
508 h = 31*h + c1
509 h = 31*h + c2
510 } else {
511 h = 31*h + r // r may be U+FFFD
512 }
513 }
514 return h
Alan Donovan312d1a52017-10-02 10:10:28 -0400515}
516
alandonovan04aba6e2018-11-05 17:45:33 -0500517// https://github.com/google/starlark-go/blob/master/doc/spec.md#int
Alan Donovan312d1a52017-10-02 10:10:28 -0400518func int_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
519 var x Value = zero
520 var base Value
alandonovan9413ca22018-03-19 13:46:37 -0400521 if err := UnpackArgs("int", args, kwargs, "x", &x, "base?", &base); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -0400522 return nil, err
523 }
524
Alan Donovan312d1a52017-10-02 10:10:28 -0400525 if s, ok := AsString(x); ok {
526 b := 10
527 if base != nil {
528 var err error
529 b, err = AsInt32(base)
alandonovan28488fa2021-01-25 14:35:08 -0500530 if err != nil {
531 return nil, fmt.Errorf("int: for base, got %s, want int", base.Type())
532 }
533 if b != 0 && (b < 2 || b > 36) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400534 return nil, fmt.Errorf("int: base must be an integer >= 2 && <= 36")
535 }
536 }
alandonovan28488fa2021-01-25 14:35:08 -0500537 res := parseInt(s, b)
538 if res == nil {
539 return nil, fmt.Errorf("int: invalid literal with base %d: %s", b, s)
Alan Donovan312d1a52017-10-02 10:10:28 -0400540 }
alandonovan28488fa2021-01-25 14:35:08 -0500541 return res, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400542 }
543
544 if base != nil {
545 return nil, fmt.Errorf("int: can't convert non-string with explicit base")
546 }
alandonovan05f260d2017-10-18 12:43:53 -0400547
548 if b, ok := x.(Bool); ok {
549 if b {
550 return one, nil
551 } else {
552 return zero, nil
553 }
554 }
555
556 i, err := NumberToInt(x)
Alan Donovan312d1a52017-10-02 10:10:28 -0400557 if err != nil {
558 return nil, fmt.Errorf("int: %s", err)
559 }
560 return i, nil
561}
562
alandonovan28488fa2021-01-25 14:35:08 -0500563// parseInt defines the behavior of int(string, base=int). It returns nil on error.
564func parseInt(s string, base int) Value {
565 // remove sign
566 var neg bool
567 if s != "" {
568 if s[0] == '+' {
569 s = s[1:]
570 } else if s[0] == '-' {
571 neg = true
572 s = s[1:]
573 }
574 }
575
576 // remove optional base prefix
577 baseprefix := 0
578 if len(s) > 1 && s[0] == '0' {
579 if len(s) > 2 {
580 switch s[1] {
581 case 'o', 'O':
582 baseprefix = 8
583 case 'x', 'X':
584 baseprefix = 16
585 case 'b', 'B':
586 baseprefix = 2
587 }
588 }
589 if baseprefix != 0 {
590 // Remove the base prefix if it matches
591 // the explicit base, or if base=0.
592 if base == 0 || baseprefix == base {
593 base = baseprefix
594 s = s[2:]
595 }
596 } else {
597 // For automatic base detection,
598 // a string starting with zero
599 // must be all zeros.
600 // Thus we reject int("0755", 0).
601 if base == 0 {
602 for i := 1; i < len(s); i++ {
603 if s[i] != '0' {
604 return nil
605 }
606 }
607 return zero
608 }
609 }
610 }
611 if base == 0 {
612 base = 10
613 }
614
615 // we explicitly handled sign above.
616 // if a sign remains, it is invalid.
617 if s != "" && (s[0] == '-' || s[0] == '+') {
618 return nil
619 }
620
621 // s has no sign or base prefix.
622 if i, ok := new(big.Int).SetString(s, base); ok {
623 res := MakeBigInt(i)
624 if neg {
625 res = zero.Sub(res)
626 }
627 return res
628 }
629
630 return nil
631}
632
alandonovan04aba6e2018-11-05 17:45:33 -0500633// https://github.com/google/starlark-go/blob/master/doc/spec.md#len
Alan Donovan312d1a52017-10-02 10:10:28 -0400634func len_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
635 var x Value
636 if err := UnpackPositionalArgs("len", args, kwargs, 1, &x); err != nil {
637 return nil, err
638 }
639 len := Len(x)
640 if len < 0 {
alandonovanf223ef82019-03-12 16:06:40 -0400641 return nil, fmt.Errorf("len: value of type %s has no len", x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400642 }
643 return MakeInt(len), nil
644}
645
alandonovan04aba6e2018-11-05 17:45:33 -0500646// https://github.com/google/starlark-go/blob/master/doc/spec.md#list
Alan Donovan312d1a52017-10-02 10:10:28 -0400647func list(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
648 var iterable Iterable
649 if err := UnpackPositionalArgs("list", args, kwargs, 0, &iterable); err != nil {
650 return nil, err
651 }
652 var elems []Value
653 if iterable != nil {
654 iter := iterable.Iterate()
655 defer iter.Done()
656 if n := Len(iterable); n > 0 {
657 elems = make([]Value, 0, n) // preallocate if length known
658 }
659 var x Value
660 for iter.Next(&x) {
661 elems = append(elems, x)
662 }
663 }
664 return NewList(elems), nil
665}
666
alandonovan04aba6e2018-11-05 17:45:33 -0500667// https://github.com/google/starlark-go/blob/master/doc/spec.md#min
alandonovan34f02112019-04-19 14:28:27 -0400668func minmax(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400669 if len(args) == 0 {
alandonovan34f02112019-04-19 14:28:27 -0400670 return nil, fmt.Errorf("%s requires at least one positional argument", b.Name())
Alan Donovan312d1a52017-10-02 10:10:28 -0400671 }
672 var keyFunc Callable
alandonovan34f02112019-04-19 14:28:27 -0400673 if err := UnpackArgs(b.Name(), nil, kwargs, "key?", &keyFunc); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -0400674 return nil, err
675 }
676 var op syntax.Token
alandonovan34f02112019-04-19 14:28:27 -0400677 if b.Name() == "max" {
Alan Donovan312d1a52017-10-02 10:10:28 -0400678 op = syntax.GT
679 } else {
680 op = syntax.LT
681 }
682 var iterable Value
683 if len(args) == 1 {
684 iterable = args[0]
685 } else {
686 iterable = args
687 }
688 iter := Iterate(iterable)
689 if iter == nil {
alandonovan34f02112019-04-19 14:28:27 -0400690 return nil, fmt.Errorf("%s: %s value is not iterable", b.Name(), iterable.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400691 }
692 defer iter.Done()
693 var extremum Value
694 if !iter.Next(&extremum) {
alandonovan34f02112019-04-19 14:28:27 -0400695 return nil, nameErr(b, "argument is an empty sequence")
Alan Donovan312d1a52017-10-02 10:10:28 -0400696 }
697
698 var extremeKey Value
699 var keyargs Tuple
700 if keyFunc == nil {
701 extremeKey = extremum
702 } else {
703 keyargs = Tuple{extremum}
704 res, err := Call(thread, keyFunc, keyargs, nil)
705 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -0400706 return nil, err // to preserve backtrace, don't modify error
Alan Donovan312d1a52017-10-02 10:10:28 -0400707 }
708 extremeKey = res
709 }
710
711 var x Value
712 for iter.Next(&x) {
713 var key Value
714 if keyFunc == nil {
715 key = x
716 } else {
717 keyargs[0] = x
718 res, err := Call(thread, keyFunc, keyargs, nil)
719 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -0400720 return nil, err // to preserve backtrace, don't modify error
Alan Donovan312d1a52017-10-02 10:10:28 -0400721 }
722 key = res
723 }
724
725 if ok, err := Compare(op, key, extremeKey); err != nil {
alandonovan34f02112019-04-19 14:28:27 -0400726 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -0400727 } else if ok {
728 extremum = x
729 extremeKey = key
730 }
731 }
732 return extremum, nil
733}
734
alandonovan04aba6e2018-11-05 17:45:33 -0500735// https://github.com/google/starlark-go/blob/master/doc/spec.md#ord
Alan Donovan312d1a52017-10-02 10:10:28 -0400736func ord(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
737 if len(kwargs) > 0 {
738 return nil, fmt.Errorf("ord does not accept keyword arguments")
739 }
740 if len(args) != 1 {
741 return nil, fmt.Errorf("ord: got %d arguments, want 1", len(args))
742 }
alandonovanebe61bd2021-02-12 16:57:32 -0500743 switch x := args[0].(type) {
744 case String:
745 // ord(string) returns int value of sole rune.
746 s := string(x)
747 r, sz := utf8.DecodeRuneInString(s)
748 if sz == 0 || sz != len(s) {
749 n := utf8.RuneCountInString(s)
750 return nil, fmt.Errorf("ord: string encodes %d Unicode code points, want 1", n)
751 }
752 return MakeInt(int(r)), nil
753
754 case Bytes:
755 // ord(bytes) returns int value of sole byte.
756 if len(x) != 1 {
757 return nil, fmt.Errorf("ord: bytes has length %d, want 1", len(x))
758 }
759 return MakeInt(int(x[0])), nil
760 default:
761 return nil, fmt.Errorf("ord: got %s, want string or bytes", x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400762 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400763}
764
alandonovan04aba6e2018-11-05 17:45:33 -0500765// https://github.com/google/starlark-go/blob/master/doc/spec.md#print
alandonovan34f02112019-04-19 14:28:27 -0400766func print(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan88085a42018-12-14 19:09:49 -0500767 sep := " "
768 if err := UnpackArgs("print", nil, kwargs, "sep?", &sep); err != nil {
769 return nil, err
770 }
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800771 buf := new(strings.Builder)
alandonovan88085a42018-12-14 19:09:49 -0500772 for i, v := range args {
773 if i > 0 {
774 buf.WriteString(sep)
775 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400776 if s, ok := AsString(v); ok {
777 buf.WriteString(s)
alandonovanebe61bd2021-02-12 16:57:32 -0500778 } else if b, ok := v.(Bytes); ok {
779 buf.WriteString(string(b))
Alan Donovan312d1a52017-10-02 10:10:28 -0400780 } else {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800781 writeValue(buf, v, nil)
Alan Donovan312d1a52017-10-02 10:10:28 -0400782 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400783 }
784
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800785 s := buf.String()
Alan Donovan312d1a52017-10-02 10:10:28 -0400786 if thread.Print != nil {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800787 thread.Print(thread, s)
Alan Donovan312d1a52017-10-02 10:10:28 -0400788 } else {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800789 fmt.Fprintln(os.Stderr, s)
Alan Donovan312d1a52017-10-02 10:10:28 -0400790 }
791 return None, nil
792}
793
alandonovan04aba6e2018-11-05 17:45:33 -0500794// https://github.com/google/starlark-go/blob/master/doc/spec.md#range
alandonovan34f02112019-04-19 14:28:27 -0400795func range_(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400796 var start, stop, step int
797 step = 1
798 if err := UnpackPositionalArgs("range", args, kwargs, 1, &start, &stop, &step); err != nil {
799 return nil, err
800 }
alandonovan05f260d2017-10-18 12:43:53 -0400801
Josh Bleecher Snyderd5c553a2019-01-16 12:51:06 -0800802 if len(args) == 1 {
Alan Donovan312d1a52017-10-02 10:10:28 -0400803 // range(stop)
804 start, stop = 0, start
Josh Bleecher Snyderd5c553a2019-01-16 12:51:06 -0800805 }
806 if step == 0 {
807 // we were given range(start, stop, 0)
alandonovan34f02112019-04-19 14:28:27 -0400808 return nil, nameErr(b, "step argument must not be zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400809 }
alandonovan05f260d2017-10-18 12:43:53 -0400810
Josh Bleecher Snyderd5c553a2019-01-16 12:51:06 -0800811 return rangeValue{start: start, stop: stop, step: step, len: rangeLen(start, stop, step)}, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400812}
813
alandonovan05f260d2017-10-18 12:43:53 -0400814// A rangeValue is a comparable, immutable, indexable sequence of integers
815// defined by the three parameters to a range(...) call.
816// Invariant: step != 0.
817type rangeValue struct{ start, stop, step, len int }
818
819var (
820 _ Indexable = rangeValue{}
821 _ Sequence = rangeValue{}
822 _ Comparable = rangeValue{}
Nick Santosd3cd7362018-05-18 12:58:53 -0400823 _ Sliceable = rangeValue{}
alandonovan05f260d2017-10-18 12:43:53 -0400824)
825
826func (r rangeValue) Len() int { return r.len }
827func (r rangeValue) Index(i int) Value { return MakeInt(r.start + i*r.step) }
828func (r rangeValue) Iterate() Iterator { return &rangeIterator{r, 0} }
Nick Santosd3cd7362018-05-18 12:58:53 -0400829
Josh Bleecher Snyderd5c553a2019-01-16 12:51:06 -0800830// rangeLen calculates the length of a range with the provided start, stop, and step.
831// caller must ensure that step is non-zero.
832func rangeLen(start, stop, step int) int {
833 switch {
834 case step > 0:
835 if stop > start {
836 return (stop-1-start)/step + 1
837 }
838 case step < 0:
839 if start > stop {
840 return (start-1-stop)/-step + 1
841 }
842 default:
843 panic("rangeLen: zero step")
844 }
845 return 0
846}
847
Nick Santosd3cd7362018-05-18 12:58:53 -0400848func (r rangeValue) Slice(start, end, step int) Value {
849 newStart := r.start + r.step*start
850 newStop := r.start + r.step*end
851 newStep := r.step * step
Nick Santosd3cd7362018-05-18 12:58:53 -0400852 return rangeValue{
853 start: newStart,
854 stop: newStop,
855 step: newStep,
Josh Bleecher Snyderd5c553a2019-01-16 12:51:06 -0800856 len: rangeLen(newStart, newStop, newStep),
Nick Santosd3cd7362018-05-18 12:58:53 -0400857 }
858}
859
860func (r rangeValue) Freeze() {} // immutable
alandonovan05f260d2017-10-18 12:43:53 -0400861func (r rangeValue) String() string {
862 if r.step != 1 {
863 return fmt.Sprintf("range(%d, %d, %d)", r.start, r.stop, r.step)
864 } else if r.start != 0 {
865 return fmt.Sprintf("range(%d, %d)", r.start, r.stop)
866 } else {
867 return fmt.Sprintf("range(%d)", r.stop)
868 }
869}
870func (r rangeValue) Type() string { return "range" }
871func (r rangeValue) Truth() Bool { return r.len > 0 }
872func (r rangeValue) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: range") }
873
874func (x rangeValue) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) {
875 y := y_.(rangeValue)
876 switch op {
877 case syntax.EQL:
878 return rangeEqual(x, y), nil
879 case syntax.NEQ:
880 return !rangeEqual(x, y), nil
881 default:
882 return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type())
883 }
884}
885
886func rangeEqual(x, y rangeValue) bool {
887 // Two ranges compare equal if they denote the same sequence.
alandonovan59a38fd2018-11-02 11:35:39 -0400888 if x.len != y.len {
889 return false // sequences differ in length
890 }
891 if x.len == 0 {
892 return true // both sequences are empty
893 }
894 if x.start != y.start {
895 return false // first element differs
896 }
897 return x.len == 1 || x.step == y.step
alandonovan05f260d2017-10-18 12:43:53 -0400898}
899
900func (r rangeValue) contains(x Int) bool {
901 x32, err := AsInt32(x)
902 if err != nil {
903 return false // out of range
904 }
905 delta := x32 - r.start
906 quo, rem := delta/r.step, delta%r.step
907 return rem == 0 && 0 <= quo && quo < r.len
908}
909
910type rangeIterator struct {
911 r rangeValue
912 i int
913}
914
915func (it *rangeIterator) Next(p *Value) bool {
916 if it.i < it.r.len {
917 *p = it.r.Index(it.i)
918 it.i++
919 return true
920 }
921 return false
922}
923func (*rangeIterator) Done() {}
924
alandonovan04aba6e2018-11-05 17:45:33 -0500925// https://github.com/google/starlark-go/blob/master/doc/spec.md#repr
Alan Donovan312d1a52017-10-02 10:10:28 -0400926func repr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
927 var x Value
928 if err := UnpackPositionalArgs("repr", args, kwargs, 1, &x); err != nil {
929 return nil, err
930 }
931 return String(x.String()), nil
932}
933
alandonovan04aba6e2018-11-05 17:45:33 -0500934// https://github.com/google/starlark-go/blob/master/doc/spec.md#reversed
Alan Donovan312d1a52017-10-02 10:10:28 -0400935func reversed(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
936 var iterable Iterable
937 if err := UnpackPositionalArgs("reversed", args, kwargs, 1, &iterable); err != nil {
938 return nil, err
939 }
940 iter := iterable.Iterate()
941 defer iter.Done()
942 var elems []Value
943 if n := Len(args[0]); n >= 0 {
944 elems = make([]Value, 0, n) // preallocate if length known
945 }
946 var x Value
947 for iter.Next(&x) {
948 elems = append(elems, x)
949 }
950 n := len(elems)
951 for i := 0; i < n>>1; i++ {
952 elems[i], elems[n-1-i] = elems[n-1-i], elems[i]
953 }
954 return NewList(elems), nil
955}
956
alandonovan04aba6e2018-11-05 17:45:33 -0500957// https://github.com/google/starlark-go/blob/master/doc/spec.md#set
alandonovan34f02112019-04-19 14:28:27 -0400958func set(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400959 var iterable Iterable
960 if err := UnpackPositionalArgs("set", args, kwargs, 0, &iterable); err != nil {
961 return nil, err
962 }
963 set := new(Set)
964 if iterable != nil {
965 iter := iterable.Iterate()
966 defer iter.Done()
967 var x Value
968 for iter.Next(&x) {
969 if err := set.Insert(x); err != nil {
alandonovan34f02112019-04-19 14:28:27 -0400970 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -0400971 }
972 }
973 }
974 return set, nil
975}
976
alandonovan04aba6e2018-11-05 17:45:33 -0500977// https://github.com/google/starlark-go/blob/master/doc/spec.md#sorted
Alan Donovan312d1a52017-10-02 10:10:28 -0400978func sorted(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovandaf30b62019-05-03 16:35:58 -0400979 // Oddly, Python's sorted permits all arguments to be positional, thus so do we.
Alan Donovan312d1a52017-10-02 10:10:28 -0400980 var iterable Iterable
alandonovan23d0c962017-10-18 14:09:28 -0400981 var key Callable
Alan Donovan312d1a52017-10-02 10:10:28 -0400982 var reverse bool
983 if err := UnpackArgs("sorted", args, kwargs,
984 "iterable", &iterable,
alandonovan23d0c962017-10-18 14:09:28 -0400985 "key?", &key,
Alan Donovan312d1a52017-10-02 10:10:28 -0400986 "reverse?", &reverse,
987 ); err != nil {
988 return nil, err
989 }
990
991 iter := iterable.Iterate()
992 defer iter.Done()
alandonovan23d0c962017-10-18 14:09:28 -0400993 var values []Value
Alan Donovan312d1a52017-10-02 10:10:28 -0400994 if n := Len(iterable); n > 0 {
alandonovan23d0c962017-10-18 14:09:28 -0400995 values = make(Tuple, 0, n) // preallocate if length is known
Alan Donovan312d1a52017-10-02 10:10:28 -0400996 }
997 var x Value
998 for iter.Next(&x) {
alandonovan23d0c962017-10-18 14:09:28 -0400999 values = append(values, x)
Alan Donovan312d1a52017-10-02 10:10:28 -04001000 }
alandonovan23d0c962017-10-18 14:09:28 -04001001
1002 // Derive keys from values by applying key function.
1003 var keys []Value
1004 if key != nil {
1005 keys = make([]Value, len(values))
1006 for i, v := range values {
1007 k, err := Call(thread, key, Tuple{v}, nil)
1008 if err != nil {
1009 return nil, err // to preserve backtrace, don't modify error
1010 }
1011 keys[i] = k
1012 }
1013 }
1014
1015 slice := &sortSlice{keys: keys, values: values}
Alan Donovan312d1a52017-10-02 10:10:28 -04001016 if reverse {
alandonovan23d0c962017-10-18 14:09:28 -04001017 sort.Stable(sort.Reverse(slice))
Alan Donovan312d1a52017-10-02 10:10:28 -04001018 } else {
alandonovan23d0c962017-10-18 14:09:28 -04001019 sort.Stable(slice)
Alan Donovan312d1a52017-10-02 10:10:28 -04001020 }
alandonovan23d0c962017-10-18 14:09:28 -04001021 return NewList(slice.values), slice.err
Alan Donovan312d1a52017-10-02 10:10:28 -04001022}
1023
1024type sortSlice struct {
alandonovan23d0c962017-10-18 14:09:28 -04001025 keys []Value // nil => values[i] is key
1026 values []Value
Alan Donovan312d1a52017-10-02 10:10:28 -04001027 err error
Alan Donovan312d1a52017-10-02 10:10:28 -04001028}
1029
alandonovan23d0c962017-10-18 14:09:28 -04001030func (s *sortSlice) Len() int { return len(s.values) }
Alan Donovan312d1a52017-10-02 10:10:28 -04001031func (s *sortSlice) Less(i, j int) bool {
alandonovan23d0c962017-10-18 14:09:28 -04001032 keys := s.keys
1033 if s.keys == nil {
1034 keys = s.values
Alan Donovan312d1a52017-10-02 10:10:28 -04001035 }
alandonovan23d0c962017-10-18 14:09:28 -04001036 ok, err := Compare(syntax.LT, keys[i], keys[j])
Diego Siqueira6cae3352017-10-08 01:16:14 +02001037 if err != nil {
1038 s.err = err
1039 }
1040 return ok
Alan Donovan312d1a52017-10-02 10:10:28 -04001041}
1042func (s *sortSlice) Swap(i, j int) {
alandonovan23d0c962017-10-18 14:09:28 -04001043 if s.keys != nil {
1044 s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
1045 }
1046 s.values[i], s.values[j] = s.values[j], s.values[i]
Alan Donovan312d1a52017-10-02 10:10:28 -04001047}
1048
alandonovan04aba6e2018-11-05 17:45:33 -05001049// https://github.com/google/starlark-go/blob/master/doc/spec.md#str
Alan Donovan312d1a52017-10-02 10:10:28 -04001050func str(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
1051 if len(kwargs) > 0 {
1052 return nil, fmt.Errorf("str does not accept keyword arguments")
1053 }
1054 if len(args) != 1 {
1055 return nil, fmt.Errorf("str: got %d arguments, want exactly 1", len(args))
1056 }
alandonovanebe61bd2021-02-12 16:57:32 -05001057 switch x := args[0].(type) {
1058 case String:
1059 return x, nil
1060 case Bytes:
1061 // Invalid encodings are replaced by that of U+FFFD.
1062 return String(utf8Transcode(string(x))), nil
1063 default:
1064 return String(x.String()), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001065 }
alandonovanebe61bd2021-02-12 16:57:32 -05001066}
1067
1068// utf8Transcode returns the UTF-8-to-UTF-8 transcoding of s.
1069// The effect is that each code unit that is part of an
1070// invalid sequence is replaced by U+FFFD.
1071func utf8Transcode(s string) string {
1072 if utf8.ValidString(s) {
1073 return s
1074 }
1075 var out strings.Builder
1076 for _, r := range s {
1077 out.WriteRune(r)
1078 }
1079 return out.String()
Alan Donovan312d1a52017-10-02 10:10:28 -04001080}
1081
alandonovan04aba6e2018-11-05 17:45:33 -05001082// https://github.com/google/starlark-go/blob/master/doc/spec.md#tuple
Alan Donovan312d1a52017-10-02 10:10:28 -04001083func tuple(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
1084 var iterable Iterable
1085 if err := UnpackPositionalArgs("tuple", args, kwargs, 0, &iterable); err != nil {
1086 return nil, err
1087 }
1088 if len(args) == 0 {
1089 return Tuple(nil), nil
1090 }
1091 iter := iterable.Iterate()
1092 defer iter.Done()
1093 var elems Tuple
1094 if n := Len(iterable); n > 0 {
1095 elems = make(Tuple, 0, n) // preallocate if length is known
1096 }
1097 var x Value
1098 for iter.Next(&x) {
1099 elems = append(elems, x)
1100 }
1101 return elems, nil
1102}
1103
alandonovan04aba6e2018-11-05 17:45:33 -05001104// https://github.com/google/starlark-go/blob/master/doc/spec.md#type
Alan Donovan312d1a52017-10-02 10:10:28 -04001105func type_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
1106 if len(kwargs) > 0 {
1107 return nil, fmt.Errorf("type does not accept keyword arguments")
1108 }
1109 if len(args) != 1 {
1110 return nil, fmt.Errorf("type: got %d arguments, want exactly 1", len(args))
1111 }
1112 return String(args[0].Type()), nil
1113}
1114
alandonovan04aba6e2018-11-05 17:45:33 -05001115// https://github.com/google/starlark-go/blob/master/doc/spec.md#zip
Alan Donovan312d1a52017-10-02 10:10:28 -04001116func zip(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
1117 if len(kwargs) > 0 {
1118 return nil, fmt.Errorf("zip does not accept keyword arguments")
1119 }
1120 rows, cols := 0, len(args)
1121 iters := make([]Iterator, cols)
kostya-sh25a152d2017-10-17 16:25:27 +01001122 defer func() {
1123 for _, iter := range iters {
1124 if iter != nil {
1125 iter.Done()
1126 }
1127 }
1128 }()
Alan Donovan312d1a52017-10-02 10:10:28 -04001129 for i, seq := range args {
1130 it := Iterate(seq)
1131 if it == nil {
1132 return nil, fmt.Errorf("zip: argument #%d is not iterable: %s", i+1, seq.Type())
1133 }
1134 iters[i] = it
1135 n := Len(seq)
Alan Donovan312d1a52017-10-02 10:10:28 -04001136 if i == 0 || n < rows {
kostya-sh5cb1ab52017-10-18 19:41:49 +01001137 rows = n // possibly -1
Alan Donovan312d1a52017-10-02 10:10:28 -04001138 }
1139 }
kostya-sh5cb1ab52017-10-18 19:41:49 +01001140 var result []Value
1141 if rows >= 0 {
1142 // length known
1143 result = make([]Value, rows)
1144 array := make(Tuple, cols*rows) // allocate a single backing array
1145 for i := 0; i < rows; i++ {
1146 tuple := array[:cols:cols]
1147 array = array[cols:]
1148 for j, iter := range iters {
1149 iter.Next(&tuple[j])
1150 }
1151 result[i] = tuple
Alan Donovan312d1a52017-10-02 10:10:28 -04001152 }
kostya-sh5cb1ab52017-10-18 19:41:49 +01001153 } else {
1154 // length not known
1155 outer:
1156 for {
1157 tuple := make(Tuple, cols)
1158 for i, iter := range iters {
1159 if !iter.Next(&tuple[i]) {
1160 break outer
1161 }
1162 }
1163 result = append(result, tuple)
1164 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001165 }
1166 return NewList(result), nil
1167}
1168
1169// ---- methods of built-in types ---
1170
alandonovan04aba6e2018-11-05 17:45:33 -05001171// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·get
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001172func dict_get(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001173 var key, dflt Value
alandonovan34f02112019-04-19 14:28:27 -04001174 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001175 return nil, err
1176 }
alandonovan34f02112019-04-19 14:28:27 -04001177 if v, ok, err := b.Receiver().(*Dict).Get(key); err != nil {
1178 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001179 } else if ok {
1180 return v, nil
1181 } else if dflt != nil {
1182 return dflt, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001183 }
Diego Siqueira6cae3352017-10-08 01:16:14 +02001184 return None, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001185}
1186
alandonovan04aba6e2018-11-05 17:45:33 -05001187// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·clear
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001188func dict_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001189 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001190 return nil, err
1191 }
alandonovan34f02112019-04-19 14:28:27 -04001192 return None, b.Receiver().(*Dict).Clear()
Alan Donovan312d1a52017-10-02 10:10:28 -04001193}
1194
alandonovan04aba6e2018-11-05 17:45:33 -05001195// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·items
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001196func dict_items(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001197 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001198 return nil, err
1199 }
alandonovan34f02112019-04-19 14:28:27 -04001200 items := b.Receiver().(*Dict).Items()
Alan Donovan312d1a52017-10-02 10:10:28 -04001201 res := make([]Value, len(items))
1202 for i, item := range items {
1203 res[i] = item // convert [2]Value to Value
1204 }
1205 return NewList(res), nil
1206}
1207
alandonovan04aba6e2018-11-05 17:45:33 -05001208// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·keys
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001209func dict_keys(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001210 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001211 return nil, err
1212 }
alandonovan34f02112019-04-19 14:28:27 -04001213 return NewList(b.Receiver().(*Dict).Keys()), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001214}
1215
alandonovan04aba6e2018-11-05 17:45:33 -05001216// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·pop
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001217func dict_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001218 var k, d Value
alandonovan34f02112019-04-19 14:28:27 -04001219 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &k, &d); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001220 return nil, err
1221 }
alandonovan34f02112019-04-19 14:28:27 -04001222 if v, found, err := b.Receiver().(*Dict).Delete(k); err != nil {
1223 return nil, nameErr(b, err) // dict is frozen or key is unhashable
Alan Donovan312d1a52017-10-02 10:10:28 -04001224 } else if found {
1225 return v, nil
1226 } else if d != nil {
1227 return d, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001228 }
alandonovan34f02112019-04-19 14:28:27 -04001229 return nil, nameErr(b, "missing key")
Alan Donovan312d1a52017-10-02 10:10:28 -04001230}
1231
alandonovan04aba6e2018-11-05 17:45:33 -05001232// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·popitem
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001233func dict_popitem(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001234 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001235 return nil, err
1236 }
alandonovan34f02112019-04-19 14:28:27 -04001237 recv := b.Receiver().(*Dict)
Alan Donovan312d1a52017-10-02 10:10:28 -04001238 k, ok := recv.ht.first()
1239 if !ok {
alandonovan34f02112019-04-19 14:28:27 -04001240 return nil, nameErr(b, "empty dict")
Alan Donovan312d1a52017-10-02 10:10:28 -04001241 }
1242 v, _, err := recv.Delete(k)
1243 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001244 return nil, nameErr(b, err) // dict is frozen
Alan Donovan312d1a52017-10-02 10:10:28 -04001245 }
1246 return Tuple{k, v}, nil
1247}
1248
alandonovan04aba6e2018-11-05 17:45:33 -05001249// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·setdefault
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001250func dict_setdefault(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001251 var key, dflt Value = nil, None
alandonovan34f02112019-04-19 14:28:27 -04001252 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001253 return nil, err
1254 }
alandonovan34f02112019-04-19 14:28:27 -04001255 dict := b.Receiver().(*Dict)
Alan Donovan312d1a52017-10-02 10:10:28 -04001256 if v, ok, err := dict.Get(key); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001257 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001258 } else if ok {
1259 return v, nil
alandonovan34f02112019-04-19 14:28:27 -04001260 } else if err := dict.SetKey(key, dflt); err != nil {
1261 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001262 } else {
alandonovan34f02112019-04-19 14:28:27 -04001263 return dflt, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001264 }
1265}
1266
alandonovan04aba6e2018-11-05 17:45:33 -05001267// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001268func dict_update(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001269 if len(args) > 1 {
1270 return nil, fmt.Errorf("update: got %d arguments, want at most 1", len(args))
1271 }
alandonovan34f02112019-04-19 14:28:27 -04001272 if err := updateDict(b.Receiver().(*Dict), args, kwargs); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001273 return nil, fmt.Errorf("update: %v", err)
1274 }
1275 return None, nil
1276}
1277
alandonovan04aba6e2018-11-05 17:45:33 -05001278// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001279func dict_values(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001280 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001281 return nil, err
1282 }
alandonovan34f02112019-04-19 14:28:27 -04001283 items := b.Receiver().(*Dict).Items()
Alan Donovan312d1a52017-10-02 10:10:28 -04001284 res := make([]Value, len(items))
1285 for i, item := range items {
1286 res[i] = item[1]
1287 }
1288 return NewList(res), nil
1289}
1290
alandonovan04aba6e2018-11-05 17:45:33 -05001291// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·append
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001292func list_append(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001293 var object Value
alandonovan34f02112019-04-19 14:28:27 -04001294 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &object); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001295 return nil, err
1296 }
alandonovan34f02112019-04-19 14:28:27 -04001297 recv := b.Receiver().(*List)
alandonovan7c0e5a32018-11-02 15:38:05 -04001298 if err := recv.checkMutable("append to"); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001299 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001300 }
1301 recv.elems = append(recv.elems, object)
1302 return None, nil
1303}
1304
alandonovan04aba6e2018-11-05 17:45:33 -05001305// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·clear
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001306func list_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001307 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001308 return nil, err
1309 }
alandonovan34f02112019-04-19 14:28:27 -04001310 if err := b.Receiver().(*List).Clear(); err != nil {
1311 return nil, nameErr(b, err)
1312 }
1313 return None, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001314}
1315
alandonovan04aba6e2018-11-05 17:45:33 -05001316// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·extend
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001317func list_extend(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001318 recv := b.Receiver().(*List)
Alan Donovan312d1a52017-10-02 10:10:28 -04001319 var iterable Iterable
alandonovan34f02112019-04-19 14:28:27 -04001320 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001321 return nil, err
1322 }
alandonovan7c0e5a32018-11-02 15:38:05 -04001323 if err := recv.checkMutable("extend"); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001324 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001325 }
1326 listExtend(recv, iterable)
1327 return None, nil
1328}
1329
alandonovan04aba6e2018-11-05 17:45:33 -05001330// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·index
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001331func list_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001332 var value, start_, end_ Value
alandonovan34f02112019-04-19 14:28:27 -04001333 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value, &start_, &end_); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001334 return nil, err
1335 }
1336
alandonovan34f02112019-04-19 14:28:27 -04001337 recv := b.Receiver().(*List)
Alan Donovan312d1a52017-10-02 10:10:28 -04001338 start, end, err := indices(start_, end_, recv.Len())
1339 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001340 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001341 }
1342
1343 for i := start; i < end; i++ {
1344 if eq, err := Equal(recv.elems[i], value); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001345 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001346 } else if eq {
1347 return MakeInt(i), nil
1348 }
1349 }
alandonovan34f02112019-04-19 14:28:27 -04001350 return nil, nameErr(b, "value not in list")
Alan Donovan312d1a52017-10-02 10:10:28 -04001351}
1352
alandonovan04aba6e2018-11-05 17:45:33 -05001353// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·insert
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001354func list_insert(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001355 recv := b.Receiver().(*List)
Alan Donovan312d1a52017-10-02 10:10:28 -04001356 var index int
1357 var object Value
alandonovan34f02112019-04-19 14:28:27 -04001358 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &index, &object); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001359 return nil, err
1360 }
alandonovan7c0e5a32018-11-02 15:38:05 -04001361 if err := recv.checkMutable("insert into"); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001362 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001363 }
1364
1365 if index < 0 {
1366 index += recv.Len()
1367 }
1368
1369 if index >= recv.Len() {
1370 // end
1371 recv.elems = append(recv.elems, object)
1372 } else {
1373 if index < 0 {
1374 index = 0 // start
1375 }
1376 recv.elems = append(recv.elems, nil)
1377 copy(recv.elems[index+1:], recv.elems[index:]) // slide up one
1378 recv.elems[index] = object
1379 }
1380 return None, nil
1381}
1382
alandonovan04aba6e2018-11-05 17:45:33 -05001383// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·remove
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001384func list_remove(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001385 recv := b.Receiver().(*List)
Alan Donovan312d1a52017-10-02 10:10:28 -04001386 var value Value
alandonovan34f02112019-04-19 14:28:27 -04001387 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001388 return nil, err
1389 }
alandonovan7c0e5a32018-11-02 15:38:05 -04001390 if err := recv.checkMutable("remove from"); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001391 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001392 }
1393 for i, elem := range recv.elems {
1394 if eq, err := Equal(elem, value); err != nil {
1395 return nil, fmt.Errorf("remove: %v", err)
1396 } else if eq {
1397 recv.elems = append(recv.elems[:i], recv.elems[i+1:]...)
1398 return None, nil
1399 }
1400 }
1401 return nil, fmt.Errorf("remove: element not found")
1402}
1403
alandonovan04aba6e2018-11-05 17:45:33 -05001404// https://github.com/google/starlark-go/blob/master/doc/spec.md#list·pop
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001405func list_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001406 recv := b.Receiver()
Alan Donovan312d1a52017-10-02 10:10:28 -04001407 list := recv.(*List)
alandonovane560c9b2019-01-23 19:08:06 -05001408 n := list.Len()
1409 i := n - 1
alandonovan34f02112019-04-19 14:28:27 -04001410 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &i); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001411 return nil, err
1412 }
alandonovane560c9b2019-01-23 19:08:06 -05001413 origI := i
1414 if i < 0 {
1415 i += n
1416 }
1417 if i < 0 || i >= n {
alandonovan34f02112019-04-19 14:28:27 -04001418 return nil, nameErr(b, outOfRange(origI, n, list))
Alan Donovan312d1a52017-10-02 10:10:28 -04001419 }
alandonovan7c0e5a32018-11-02 15:38:05 -04001420 if err := list.checkMutable("pop from"); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001421 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001422 }
alandonovane560c9b2019-01-23 19:08:06 -05001423 res := list.elems[i]
1424 list.elems = append(list.elems[:i], list.elems[i+1:]...)
Alan Donovan312d1a52017-10-02 10:10:28 -04001425 return res, nil
1426}
1427
alandonovan04aba6e2018-11-05 17:45:33 -05001428// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·capitalize
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001429func string_capitalize(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001430 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001431 return nil, err
1432 }
alandonovan34f02112019-04-19 14:28:27 -04001433 s := string(b.Receiver().(String))
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001434 res := new(strings.Builder)
alandonovan4c43ff32018-11-02 14:50:48 -04001435 res.Grow(len(s))
1436 for i, r := range s {
1437 if i == 0 {
1438 r = unicode.ToTitle(r)
1439 } else {
1440 r = unicode.ToLower(r)
1441 }
1442 res.WriteRune(r)
1443 }
1444 return String(res.String()), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001445}
1446
1447// string_iterable returns an unspecified iterable value whose iterator yields:
alandonovan0569d1c2018-03-29 14:47:42 -04001448// - elems: successive 1-byte substrings
1449// - codepoints: successive substrings that encode a single Unicode code point.
1450// - elem_ords: numeric values of successive bytes
1451// - codepoint_ords: numeric values of successive Unicode code points
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001452func string_iterable(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001453 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001454 return nil, err
1455 }
alandonovanebe61bd2021-02-12 16:57:32 -05001456 s := b.Receiver().(String)
1457 ords := b.Name()[len(b.Name())-2] == 'd'
1458 codepoints := b.Name()[0] == 'c'
1459 if codepoints {
1460 return stringCodepoints{s, ords}, nil
1461 } else {
1462 return stringElems{s, ords}, nil
1463 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001464}
1465
alandonovanebe61bd2021-02-12 16:57:32 -05001466// bytes_elems returns an unspecified iterable value whose
1467// iterator yields the int values of successive elements.
1468func bytes_elems(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
1469 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
1470 return nil, err
1471 }
1472 return bytesIterable{b.Receiver().(Bytes)}, nil
1473}
1474
1475// A bytesIterable is an iterable returned by bytes.elems(),
1476// whose iterator yields a sequence of numeric bytes values.
1477type bytesIterable struct{ bytes Bytes }
1478
1479var _ Iterable = (*bytesIterable)(nil)
1480
1481func (bi bytesIterable) String() string { return bi.bytes.String() + ".elems()" }
1482func (bi bytesIterable) Type() string { return "bytes.elems" }
1483func (bi bytesIterable) Freeze() {} // immutable
1484func (bi bytesIterable) Truth() Bool { return True }
1485func (bi bytesIterable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", bi.Type()) }
1486func (bi bytesIterable) Iterate() Iterator { return &bytesIterator{bi.bytes} }
1487
1488type bytesIterator struct{ bytes Bytes }
1489
1490func (it *bytesIterator) Next(p *Value) bool {
1491 if it.bytes == "" {
1492 return false
1493 }
1494 *p = MakeInt(int(it.bytes[0]))
1495 it.bytes = it.bytes[1:]
1496 return true
1497}
1498
1499func (*bytesIterator) Done() {}
1500
alandonovan04aba6e2018-11-05 17:45:33 -05001501// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·count
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001502func string_count(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001503 var sub string
1504 var start_, end_ Value
alandonovan34f02112019-04-19 14:28:27 -04001505 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001506 return nil, err
1507 }
1508
alandonovan34f02112019-04-19 14:28:27 -04001509 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001510 start, end, err := indices(start_, end_, len(recv))
1511 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001512 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001513 }
1514
1515 var slice string
1516 if start < end {
1517 slice = recv[start:end]
1518 }
1519 return MakeInt(strings.Count(slice, sub)), nil
1520}
1521
alandonovan04aba6e2018-11-05 17:45:33 -05001522// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalnum
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001523func string_isalnum(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001524 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001525 return nil, err
1526 }
alandonovan34f02112019-04-19 14:28:27 -04001527 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001528 for _, r := range recv {
1529 if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
1530 return False, nil
1531 }
1532 }
1533 return Bool(recv != ""), nil
1534}
1535
alandonovan04aba6e2018-11-05 17:45:33 -05001536// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalpha
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001537func string_isalpha(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001538 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001539 return nil, err
1540 }
alandonovan34f02112019-04-19 14:28:27 -04001541 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001542 for _, r := range recv {
1543 if !unicode.IsLetter(r) {
1544 return False, nil
1545 }
1546 }
1547 return Bool(recv != ""), nil
1548}
1549
alandonovan04aba6e2018-11-05 17:45:33 -05001550// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isdigit
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001551func string_isdigit(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001552 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001553 return nil, err
1554 }
alandonovan34f02112019-04-19 14:28:27 -04001555 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001556 for _, r := range recv {
1557 if !unicode.IsDigit(r) {
1558 return False, nil
1559 }
1560 }
1561 return Bool(recv != ""), nil
1562}
1563
alandonovan04aba6e2018-11-05 17:45:33 -05001564// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·islower
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001565func string_islower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001566 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001567 return nil, err
1568 }
alandonovan34f02112019-04-19 14:28:27 -04001569 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001570 return Bool(isCasedString(recv) && recv == strings.ToLower(recv)), nil
1571}
1572
alandonovan4c43ff32018-11-02 14:50:48 -04001573// isCasedString reports whether its argument contains any cased code points.
Alan Donovan312d1a52017-10-02 10:10:28 -04001574func isCasedString(s string) bool {
1575 for _, r := range s {
alandonovan4c43ff32018-11-02 14:50:48 -04001576 if isCasedRune(r) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001577 return true
1578 }
1579 }
1580 return false
1581}
1582
alandonovan4c43ff32018-11-02 14:50:48 -04001583func isCasedRune(r rune) bool {
1584 // It's unclear what the correct behavior is for a rune such as 'ffi',
1585 // a lowercase letter with no upper or title case and no SimpleFold.
1586 return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || unicode.SimpleFold(r) != r
1587}
1588
alandonovan04aba6e2018-11-05 17:45:33 -05001589// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isspace
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001590func string_isspace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001591 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001592 return nil, err
1593 }
alandonovan34f02112019-04-19 14:28:27 -04001594 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001595 for _, r := range recv {
1596 if !unicode.IsSpace(r) {
1597 return False, nil
1598 }
1599 }
1600 return Bool(recv != ""), nil
1601}
1602
alandonovan04aba6e2018-11-05 17:45:33 -05001603// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·istitle
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001604func string_istitle(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001605 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001606 return nil, err
1607 }
alandonovan34f02112019-04-19 14:28:27 -04001608 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001609
1610 // Python semantics differ from x==strings.{To,}Title(x) in Go:
1611 // "uppercase characters may only follow uncased characters and
1612 // lowercase characters only cased ones."
1613 var cased, prevCased bool
1614 for _, r := range recv {
alandonovan4c43ff32018-11-02 14:50:48 -04001615 if 'A' <= r && r <= 'Z' || unicode.IsTitle(r) { // e.g. "Dž"
Alan Donovan312d1a52017-10-02 10:10:28 -04001616 if prevCased {
1617 return False, nil
1618 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001619 prevCased = true
alandonovan4c43ff32018-11-02 14:50:48 -04001620 cased = true
Alan Donovan312d1a52017-10-02 10:10:28 -04001621 } else if unicode.IsLower(r) {
1622 if !prevCased {
1623 return False, nil
1624 }
1625 prevCased = true
1626 cased = true
alandonovan4c43ff32018-11-02 14:50:48 -04001627 } else if unicode.IsUpper(r) {
1628 return False, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001629 } else {
1630 prevCased = false
1631 }
1632 }
1633 return Bool(cased), nil
1634}
1635
alandonovan04aba6e2018-11-05 17:45:33 -05001636// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isupper
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001637func string_isupper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001638 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001639 return nil, err
1640 }
alandonovan34f02112019-04-19 14:28:27 -04001641 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001642 return Bool(isCasedString(recv) && recv == strings.ToUpper(recv)), nil
1643}
1644
alandonovan04aba6e2018-11-05 17:45:33 -05001645// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·find
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001646func string_find(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001647 return string_find_impl(b, args, kwargs, true, false)
Alan Donovan312d1a52017-10-02 10:10:28 -04001648}
1649
alandonovan04aba6e2018-11-05 17:45:33 -05001650// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·format
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001651func string_format(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001652 format := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001653 var auto, manual bool // kinds of positional indexing used
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001654 buf := new(strings.Builder)
Alan Donovan312d1a52017-10-02 10:10:28 -04001655 index := 0
1656 for {
alandonovan71da6622017-11-28 22:08:16 -05001657 literal := format
Alan Donovan312d1a52017-10-02 10:10:28 -04001658 i := strings.IndexByte(format, '{')
alandonovan71da6622017-11-28 22:08:16 -05001659 if i >= 0 {
1660 literal = format[:i]
Alan Donovan312d1a52017-10-02 10:10:28 -04001661 }
alandonovan71da6622017-11-28 22:08:16 -05001662
1663 // Replace "}}" with "}" in non-field portion, rejecting a lone '}'.
1664 for {
1665 j := strings.IndexByte(literal, '}')
1666 if j < 0 {
1667 buf.WriteString(literal)
1668 break
1669 }
1670 if len(literal) == j+1 || literal[j+1] != '}' {
alandonovanf223ef82019-03-12 16:06:40 -04001671 return nil, fmt.Errorf("format: single '}' in format")
alandonovan71da6622017-11-28 22:08:16 -05001672 }
1673 buf.WriteString(literal[:j+1])
1674 literal = literal[j+2:]
1675 }
1676
1677 if i < 0 {
1678 break // end of format string
1679 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001680
1681 if i+1 < len(format) && format[i+1] == '{' {
1682 // "{{" means a literal '{'
1683 buf.WriteByte('{')
1684 format = format[i+2:]
1685 continue
1686 }
1687
1688 format = format[i+1:]
1689 i = strings.IndexByte(format, '}')
1690 if i < 0 {
alandonovanf223ef82019-03-12 16:06:40 -04001691 return nil, fmt.Errorf("format: unmatched '{' in format")
Alan Donovan312d1a52017-10-02 10:10:28 -04001692 }
1693
1694 var arg Value
1695 conv := "s"
1696 var spec string
1697
1698 field := format[:i]
1699 format = format[i+1:]
1700
1701 var name string
1702 if i := strings.IndexByte(field, '!'); i < 0 {
1703 // "name" or "name:spec"
1704 if i := strings.IndexByte(field, ':'); i < 0 {
1705 name = field
1706 } else {
1707 name = field[:i]
1708 spec = field[i+1:]
1709 }
1710 } else {
1711 // "name!conv" or "name!conv:spec"
1712 name = field[:i]
1713 field = field[i+1:]
1714 // "conv" or "conv:spec"
1715 if i := strings.IndexByte(field, ':'); i < 0 {
1716 conv = field
1717 } else {
1718 conv = field[:i]
1719 spec = field[i+1:]
1720 }
1721 }
1722
1723 if name == "" {
1724 // "{}": automatic indexing
1725 if manual {
alandonovanf223ef82019-03-12 16:06:40 -04001726 return nil, fmt.Errorf("format: cannot switch from manual field specification to automatic field numbering")
Alan Donovan312d1a52017-10-02 10:10:28 -04001727 }
1728 auto = true
1729 if index >= len(args) {
alandonovanf223ef82019-03-12 16:06:40 -04001730 return nil, fmt.Errorf("format: tuple index out of range")
Alan Donovan312d1a52017-10-02 10:10:28 -04001731 }
1732 arg = args[index]
1733 index++
alandonovanbf3b5872019-01-15 09:15:30 -05001734 } else if num, ok := decimal(name); ok {
Alan Donovan312d1a52017-10-02 10:10:28 -04001735 // positional argument
1736 if auto {
alandonovanf223ef82019-03-12 16:06:40 -04001737 return nil, fmt.Errorf("format: cannot switch from automatic field numbering to manual field specification")
Alan Donovan312d1a52017-10-02 10:10:28 -04001738 }
1739 manual = true
1740 if num >= len(args) {
alandonovanf223ef82019-03-12 16:06:40 -04001741 return nil, fmt.Errorf("format: tuple index out of range")
Alan Donovan312d1a52017-10-02 10:10:28 -04001742 } else {
1743 arg = args[num]
1744 }
1745 } else {
1746 // keyword argument
1747 for _, kv := range kwargs {
1748 if string(kv[0].(String)) == name {
1749 arg = kv[1]
1750 break
1751 }
1752 }
1753 if arg == nil {
Alan Donovane3deafe2018-10-23 11:05:09 -04001754 // Starlark does not support Python's x.y or a[i] syntaxes,
alandonovan71da6622017-11-28 22:08:16 -05001755 // or nested use of {...}.
Alan Donovan312d1a52017-10-02 10:10:28 -04001756 if strings.Contains(name, ".") {
alandonovanf223ef82019-03-12 16:06:40 -04001757 return nil, fmt.Errorf("format: attribute syntax x.y is not supported in replacement fields: %s", name)
Alan Donovan312d1a52017-10-02 10:10:28 -04001758 }
1759 if strings.Contains(name, "[") {
alandonovanf223ef82019-03-12 16:06:40 -04001760 return nil, fmt.Errorf("format: element syntax a[i] is not supported in replacement fields: %s", name)
Alan Donovan312d1a52017-10-02 10:10:28 -04001761 }
alandonovan71da6622017-11-28 22:08:16 -05001762 if strings.Contains(name, "{") {
alandonovanf223ef82019-03-12 16:06:40 -04001763 return nil, fmt.Errorf("format: nested replacement fields not supported")
alandonovan71da6622017-11-28 22:08:16 -05001764 }
alandonovanf223ef82019-03-12 16:06:40 -04001765 return nil, fmt.Errorf("format: keyword %s not found", name)
Alan Donovan312d1a52017-10-02 10:10:28 -04001766 }
1767 }
1768
1769 if spec != "" {
Alan Donovane3deafe2018-10-23 11:05:09 -04001770 // Starlark does not support Python's format_spec features.
Alan Donovan312d1a52017-10-02 10:10:28 -04001771 return nil, fmt.Errorf("format spec features not supported in replacement fields: %s", spec)
1772 }
1773
1774 switch conv {
1775 case "s":
1776 if str, ok := AsString(arg); ok {
1777 buf.WriteString(str)
1778 } else {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001779 writeValue(buf, arg, nil)
Alan Donovan312d1a52017-10-02 10:10:28 -04001780 }
1781 case "r":
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001782 writeValue(buf, arg, nil)
Alan Donovan312d1a52017-10-02 10:10:28 -04001783 default:
alandonovanf223ef82019-03-12 16:06:40 -04001784 return nil, fmt.Errorf("format: unknown conversion %q", conv)
Alan Donovan312d1a52017-10-02 10:10:28 -04001785 }
1786 }
1787 return String(buf.String()), nil
1788}
1789
alandonovanbf3b5872019-01-15 09:15:30 -05001790// decimal interprets s as a sequence of decimal digits.
1791func decimal(s string) (x int, ok bool) {
1792 n := len(s)
1793 for i := 0; i < n; i++ {
1794 digit := s[i] - '0'
1795 if digit > 9 {
1796 return 0, false
1797 }
1798 x = x*10 + int(digit)
1799 if x < 0 {
1800 return 0, false // underflow
1801 }
1802 }
1803 return x, true
1804}
1805
alandonovan04aba6e2018-11-05 17:45:33 -05001806// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·index
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001807func string_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001808 return string_find_impl(b, args, kwargs, false, false)
Alan Donovan312d1a52017-10-02 10:10:28 -04001809}
1810
alandonovan04aba6e2018-11-05 17:45:33 -05001811// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·join
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001812func string_join(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001813 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001814 var iterable Iterable
alandonovan34f02112019-04-19 14:28:27 -04001815 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001816 return nil, err
1817 }
1818 iter := iterable.Iterate()
1819 defer iter.Done()
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001820 buf := new(strings.Builder)
Alan Donovan312d1a52017-10-02 10:10:28 -04001821 var x Value
1822 for i := 0; iter.Next(&x); i++ {
1823 if i > 0 {
1824 buf.WriteString(recv)
1825 }
Diego Siqueira6cae3352017-10-08 01:16:14 +02001826 s, ok := AsString(x)
1827 if !ok {
alandonovanf223ef82019-03-12 16:06:40 -04001828 return nil, fmt.Errorf("join: in list, want string, got %s", x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -04001829 }
Diego Siqueira6cae3352017-10-08 01:16:14 +02001830 buf.WriteString(s)
Alan Donovan312d1a52017-10-02 10:10:28 -04001831 }
1832 return String(buf.String()), nil
1833}
1834
alandonovan04aba6e2018-11-05 17:45:33 -05001835// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lower
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001836func string_lower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001837 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001838 return nil, err
1839 }
alandonovan34f02112019-04-19 14:28:27 -04001840 return String(strings.ToLower(string(b.Receiver().(String)))), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001841}
1842
alandonovan04aba6e2018-11-05 17:45:33 -05001843// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·partition
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001844func string_partition(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001845 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001846 var sep string
alandonovan34f02112019-04-19 14:28:27 -04001847 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sep); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001848 return nil, err
1849 }
1850 if sep == "" {
alandonovan34f02112019-04-19 14:28:27 -04001851 return nil, nameErr(b, "empty separator")
Alan Donovan312d1a52017-10-02 10:10:28 -04001852 }
1853 var i int
alandonovan34f02112019-04-19 14:28:27 -04001854 if b.Name()[0] == 'p' {
Alan Donovan312d1a52017-10-02 10:10:28 -04001855 i = strings.Index(recv, sep) // partition
1856 } else {
1857 i = strings.LastIndex(recv, sep) // rpartition
1858 }
1859 tuple := make(Tuple, 0, 3)
1860 if i < 0 {
alandonovan34f02112019-04-19 14:28:27 -04001861 if b.Name()[0] == 'p' {
Alan Donovan312d1a52017-10-02 10:10:28 -04001862 tuple = append(tuple, String(recv), String(""), String(""))
1863 } else {
1864 tuple = append(tuple, String(""), String(""), String(recv))
1865 }
1866 } else {
1867 tuple = append(tuple, String(recv[:i]), String(sep), String(recv[i+len(sep):]))
1868 }
1869 return tuple, nil
1870}
1871
alandonovan04aba6e2018-11-05 17:45:33 -05001872// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·replace
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001873func string_replace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001874 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001875 var old, new string
1876 count := -1
alandonovan34f02112019-04-19 14:28:27 -04001877 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &old, &new, &count); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001878 return nil, err
1879 }
1880 return String(strings.Replace(recv, old, new, count)), nil
1881}
1882
alandonovan04aba6e2018-11-05 17:45:33 -05001883// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rfind
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001884func string_rfind(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001885 return string_find_impl(b, args, kwargs, true, true)
Alan Donovan312d1a52017-10-02 10:10:28 -04001886}
1887
alandonovan04aba6e2018-11-05 17:45:33 -05001888// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rindex
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001889func string_rindex(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001890 return string_find_impl(b, args, kwargs, false, true)
Alan Donovan312d1a52017-10-02 10:10:28 -04001891}
1892
alandonovan04aba6e2018-11-05 17:45:33 -05001893// https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·startswith
1894// https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·endswith
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001895func string_startswith(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovanf5fb4d22018-06-25 11:31:34 -04001896 var x Value
1897 var start, end Value = None, None
alandonovan34f02112019-04-19 14:28:27 -04001898 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x, &start, &end); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001899 return nil, err
1900 }
alandonovanf5fb4d22018-06-25 11:31:34 -04001901
1902 // compute effective substring.
alandonovan34f02112019-04-19 14:28:27 -04001903 s := string(b.Receiver().(String))
alandonovanf5fb4d22018-06-25 11:31:34 -04001904 if start, end, err := indices(start, end, len(s)); err != nil {
alandonovan34f02112019-04-19 14:28:27 -04001905 return nil, nameErr(b, err)
alandonovanf5fb4d22018-06-25 11:31:34 -04001906 } else {
1907 if end < start {
1908 end = start // => empty result
1909 }
1910 s = s[start:end]
1911 }
1912
1913 f := strings.HasPrefix
alandonovan34f02112019-04-19 14:28:27 -04001914 if b.Name()[0] == 'e' { // endswith
alandonovanf5fb4d22018-06-25 11:31:34 -04001915 f = strings.HasSuffix
1916 }
1917
1918 switch x := x.(type) {
1919 case Tuple:
1920 for i, x := range x {
1921 prefix, ok := AsString(x)
1922 if !ok {
1923 return nil, fmt.Errorf("%s: want string, got %s, for element %d",
alandonovan34f02112019-04-19 14:28:27 -04001924 b.Name(), x.Type(), i)
alandonovanf5fb4d22018-06-25 11:31:34 -04001925 }
1926 if f(s, prefix) {
1927 return True, nil
1928 }
1929 }
1930 return False, nil
1931 case String:
1932 return Bool(f(s, string(x))), nil
1933 }
alandonovan34f02112019-04-19 14:28:27 -04001934 return nil, fmt.Errorf("%s: got %s, want string or tuple of string", b.Name(), x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -04001935}
1936
alandonovan04aba6e2018-11-05 17:45:33 -05001937// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·strip
1938// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lstrip
1939// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rstrip
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001940func string_strip(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04001941 var chars string
alandonovan34f02112019-04-19 14:28:27 -04001942 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &chars); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001943 return nil, err
1944 }
alandonovan34f02112019-04-19 14:28:27 -04001945 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04001946 var s string
alandonovan34f02112019-04-19 14:28:27 -04001947 switch b.Name()[0] {
Alan Donovan312d1a52017-10-02 10:10:28 -04001948 case 's': // strip
1949 if chars != "" {
1950 s = strings.Trim(recv, chars)
1951 } else {
1952 s = strings.TrimSpace(recv)
1953 }
1954 case 'l': // lstrip
1955 if chars != "" {
1956 s = strings.TrimLeft(recv, chars)
1957 } else {
1958 s = strings.TrimLeftFunc(recv, unicode.IsSpace)
1959 }
1960 case 'r': // rstrip
1961 if chars != "" {
1962 s = strings.TrimRight(recv, chars)
1963 } else {
1964 s = strings.TrimRightFunc(recv, unicode.IsSpace)
1965 }
1966 }
1967 return String(s), nil
1968}
1969
alandonovan04aba6e2018-11-05 17:45:33 -05001970// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·title
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001971func string_title(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001972 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001973 return nil, err
1974 }
alandonovan4c43ff32018-11-02 14:50:48 -04001975
alandonovan34f02112019-04-19 14:28:27 -04001976 s := string(b.Receiver().(String))
alandonovan4c43ff32018-11-02 14:50:48 -04001977
1978 // Python semantics differ from x==strings.{To,}Title(x) in Go:
1979 // "uppercase characters may only follow uncased characters and
1980 // lowercase characters only cased ones."
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001981 buf := new(strings.Builder)
alandonovan4c43ff32018-11-02 14:50:48 -04001982 buf.Grow(len(s))
1983 var prevCased bool
1984 for _, r := range s {
1985 if prevCased {
1986 r = unicode.ToLower(r)
1987 } else {
1988 r = unicode.ToTitle(r)
1989 }
1990 prevCased = isCasedRune(r)
1991 buf.WriteRune(r)
1992 }
1993 return String(buf.String()), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001994}
1995
alandonovan04aba6e2018-11-05 17:45:33 -05001996// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·upper
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00001997func string_upper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04001998 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04001999 return nil, err
2000 }
alandonovan34f02112019-04-19 14:28:27 -04002001 return String(strings.ToUpper(string(b.Receiver().(String)))), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04002002}
2003
alandonovan04aba6e2018-11-05 17:45:33 -05002004// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·split
2005// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rsplit
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00002006func string_split(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
alandonovan34f02112019-04-19 14:28:27 -04002007 recv := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04002008 var sep_ Value
2009 maxsplit := -1
alandonovan34f02112019-04-19 14:28:27 -04002010 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &sep_, &maxsplit); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002011 return nil, err
2012 }
2013
2014 var res []string
2015
2016 if sep_ == nil || sep_ == None {
2017 // special case: split on whitespace
alandonovan7b65d162017-11-01 16:35:47 -04002018 if maxsplit < 0 {
Alan Donovan312d1a52017-10-02 10:10:28 -04002019 res = strings.Fields(recv)
alandonovan34f02112019-04-19 14:28:27 -04002020 } else if b.Name() == "split" {
Garrett Squired6768aa2017-10-31 20:33:53 -07002021 res = splitspace(recv, maxsplit)
Alan Donovan312d1a52017-10-02 10:10:28 -04002022 } else { // rsplit
Garrett Squired6768aa2017-10-31 20:33:53 -07002023 res = rsplitspace(recv, maxsplit)
Alan Donovan312d1a52017-10-02 10:10:28 -04002024 }
2025
2026 } else if sep, ok := AsString(sep_); ok {
2027 if sep == "" {
Diego Siqueiraff22b092017-10-04 12:14:33 +02002028 return nil, fmt.Errorf("split: empty separator")
Alan Donovan312d1a52017-10-02 10:10:28 -04002029 }
2030 // usual case: split on non-empty separator
alandonovan7b65d162017-11-01 16:35:47 -04002031 if maxsplit < 0 {
Alan Donovan312d1a52017-10-02 10:10:28 -04002032 res = strings.Split(recv, sep)
alandonovan34f02112019-04-19 14:28:27 -04002033 } else if b.Name() == "split" {
Alan Donovan312d1a52017-10-02 10:10:28 -04002034 res = strings.SplitN(recv, sep, maxsplit+1)
2035 } else { // rsplit
2036 res = strings.Split(recv, sep)
2037 if excess := len(res) - maxsplit; excess > 0 {
2038 res[0] = strings.Join(res[:excess], sep)
2039 res = append(res[:1], res[excess:]...)
2040 }
2041 }
2042
2043 } else {
Diego Siqueiraff22b092017-10-04 12:14:33 +02002044 return nil, fmt.Errorf("split: got %s for separator, want string", sep_.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -04002045 }
2046
2047 list := make([]Value, len(res))
2048 for i, x := range res {
2049 list[i] = String(x)
2050 }
2051 return NewList(list), nil
2052}
2053
alandonovan7b65d162017-11-01 16:35:47 -04002054// Precondition: max >= 0.
Garrett Squired6768aa2017-10-31 20:33:53 -07002055func rsplitspace(s string, max int) []string {
2056 res := make([]string, 0, max+1)
2057 end := -1 // index of field end, or -1 in a region of spaces.
2058 for i := len(s); i > 0; {
2059 r, sz := utf8.DecodeLastRuneInString(s[:i])
2060 if unicode.IsSpace(r) {
2061 if end >= 0 {
2062 if len(res) == max {
2063 break // let this field run to the start
2064 }
2065 res = append(res, s[i:end])
2066 end = -1
2067 }
2068 } else if end < 0 {
2069 end = i
2070 }
2071 i -= sz
2072 }
2073 if end >= 0 {
2074 res = append(res, s[:end])
2075 }
2076
2077 resLen := len(res)
2078 for i := 0; i < resLen/2; i++ {
2079 res[i], res[resLen-1-i] = res[resLen-1-i], res[i]
2080 }
2081
2082 return res
2083}
2084
alandonovan7b65d162017-11-01 16:35:47 -04002085// Precondition: max >= 0.
Alan Donovan312d1a52017-10-02 10:10:28 -04002086func splitspace(s string, max int) []string {
2087 var res []string
2088 start := -1 // index of field start, or -1 in a region of spaces
2089 for i, r := range s {
2090 if unicode.IsSpace(r) {
2091 if start >= 0 {
Garrett Squired6768aa2017-10-31 20:33:53 -07002092 if len(res) == max {
Alan Donovan312d1a52017-10-02 10:10:28 -04002093 break // let this field run to the end
2094 }
2095 res = append(res, s[start:i])
2096 start = -1
2097 }
2098 } else if start == -1 {
2099 start = i
2100 }
2101 }
2102 if start >= 0 {
2103 res = append(res, s[start:])
2104 }
2105 return res
2106}
2107
alandonovan04aba6e2018-11-05 17:45:33 -05002108// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·splitlines
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00002109func string_splitlines(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04002110 var keepends bool
alandonovan34f02112019-04-19 14:28:27 -04002111 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &keepends); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002112 return nil, err
2113 }
Alan Donovan312d1a52017-10-02 10:10:28 -04002114 var lines []string
alandonovan34f02112019-04-19 14:28:27 -04002115 if s := string(b.Receiver().(String)); s != "" {
alandonovan266cd6f2019-02-13 19:04:37 -05002116 // TODO(adonovan): handle CRLF correctly.
2117 if keepends {
2118 lines = strings.SplitAfter(s, "\n")
2119 } else {
2120 lines = strings.Split(s, "\n")
2121 }
2122 if strings.HasSuffix(s, "\n") {
2123 lines = lines[:len(lines)-1]
2124 }
Alan Donovan312d1a52017-10-02 10:10:28 -04002125 }
2126 list := make([]Value, len(lines))
2127 for i, x := range lines {
2128 list[i] = String(x)
2129 }
2130 return NewList(list), nil
2131}
2132
alandonovan04aba6e2018-11-05 17:45:33 -05002133// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union.
Edward McFarlane8dd3e2e2020-03-06 20:57:01 +00002134func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04002135 var iterable Iterable
alandonovan34f02112019-04-19 14:28:27 -04002136 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &iterable); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002137 return nil, err
2138 }
2139 iter := iterable.Iterate()
2140 defer iter.Done()
alandonovan34f02112019-04-19 14:28:27 -04002141 union, err := b.Receiver().(*Set).Union(iter)
Alan Donovan312d1a52017-10-02 10:10:28 -04002142 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -04002143 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04002144 }
2145 return union, nil
2146}
2147
2148// Common implementation of string_{r}{find,index}.
alandonovan34f02112019-04-19 14:28:27 -04002149func string_find_impl(b *Builtin, args Tuple, kwargs []Tuple, allowError, last bool) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -04002150 var sub string
2151 var start_, end_ Value
alandonovan34f02112019-04-19 14:28:27 -04002152 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002153 return nil, err
2154 }
2155
alandonovan34f02112019-04-19 14:28:27 -04002156 s := string(b.Receiver().(String))
Alan Donovan312d1a52017-10-02 10:10:28 -04002157 start, end, err := indices(start_, end_, len(s))
2158 if err != nil {
alandonovan34f02112019-04-19 14:28:27 -04002159 return nil, nameErr(b, err)
Alan Donovan312d1a52017-10-02 10:10:28 -04002160 }
2161 var slice string
2162 if start < end {
2163 slice = s[start:end]
2164 }
2165
2166 var i int
2167 if last {
2168 i = strings.LastIndex(slice, sub)
2169 } else {
2170 i = strings.Index(slice, sub)
2171 }
2172 if i < 0 {
2173 if !allowError {
alandonovan34f02112019-04-19 14:28:27 -04002174 return nil, nameErr(b, "substring not found")
Alan Donovan312d1a52017-10-02 10:10:28 -04002175 }
2176 return MakeInt(-1), nil
2177 }
2178 return MakeInt(i + start), nil
2179}
2180
2181// Common implementation of builtin dict function and dict.update method.
2182// Precondition: len(updates) == 0 or 1.
2183func updateDict(dict *Dict, updates Tuple, kwargs []Tuple) error {
2184 if len(updates) == 1 {
2185 switch updates := updates[0].(type) {
alandonovan1ed64972019-01-04 13:04:39 -05002186 case IterableMapping:
Alan Donovan312d1a52017-10-02 10:10:28 -04002187 // Iterate over dict's key/value pairs, not just keys.
2188 for _, item := range updates.Items() {
jmillikin-stripe3ccab942018-10-05 07:09:12 -07002189 if err := dict.SetKey(item[0], item[1]); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002190 return err // dict is frozen
2191 }
2192 }
2193 default:
2194 // all other sequences
2195 iter := Iterate(updates)
2196 if iter == nil {
2197 return fmt.Errorf("got %s, want iterable", updates.Type())
2198 }
2199 defer iter.Done()
2200 var pair Value
2201 for i := 0; iter.Next(&pair); i++ {
2202 iter2 := Iterate(pair)
2203 if iter2 == nil {
2204 return fmt.Errorf("dictionary update sequence element #%d is not iterable (%s)", i, pair.Type())
2205
2206 }
2207 defer iter2.Done()
2208 len := Len(pair)
2209 if len < 0 {
2210 return fmt.Errorf("dictionary update sequence element #%d has unknown length (%s)", i, pair.Type())
2211 } else if len != 2 {
2212 return fmt.Errorf("dictionary update sequence element #%d has length %d, want 2", i, len)
2213 }
2214 var k, v Value
2215 iter2.Next(&k)
2216 iter2.Next(&v)
jmillikin-stripe3ccab942018-10-05 07:09:12 -07002217 if err := dict.SetKey(k, v); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002218 return err
2219 }
2220 }
2221 }
2222 }
2223
2224 // Then add the kwargs.
Josh Bleecher Snyder66ac3a22018-12-12 17:54:30 -08002225 before := dict.Len()
Alan Donovan312d1a52017-10-02 10:10:28 -04002226 for _, pair := range kwargs {
jmillikin-stripe3ccab942018-10-05 07:09:12 -07002227 if err := dict.SetKey(pair[0], pair[1]); err != nil {
Alan Donovan312d1a52017-10-02 10:10:28 -04002228 return err // dict is frozen
2229 }
2230 }
Josh Bleecher Snyder66ac3a22018-12-12 17:54:30 -08002231 // In the common case, each kwarg will add another dict entry.
2232 // If that's not so, check whether it is because there was a duplicate kwarg.
2233 if dict.Len() < before+len(kwargs) {
2234 keys := make(map[String]bool, len(kwargs))
2235 for _, kv := range kwargs {
2236 k := kv[0].(String)
2237 if keys[k] {
2238 return fmt.Errorf("duplicate keyword arg: %v", k)
2239 }
2240 keys[k] = true
2241 }
2242 }
Alan Donovan312d1a52017-10-02 10:10:28 -04002243
2244 return nil
2245}
alandonovan34f02112019-04-19 14:28:27 -04002246
2247// nameErr returns an error message of the form "name: msg"
2248// where name is b.Name() and msg is a string or error.
2249func nameErr(b *Builtin, msg interface{}) error {
2250 return fmt.Errorf("%s: %v", b.Name(), msg)
2251}