diff --git a/starlark/iter.go b/starlark/iter.go new file mode 100644 index 00000000..5436d9fe --- /dev/null +++ b/starlark/iter.go @@ -0,0 +1,118 @@ +// Copyright 2024 The Bazel Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.23 + +package starlark + +import ( + "fmt" + "iter" +) + +func (d *Dict) Entries() iter.Seq2[Value, Value] { return d.ht.entries } + +// Elements returns a go1.23 iterator over the elements of the list. +// +// Example: +// +// for elem := range list.Elements() { ... } +func (l *List) Elements() iter.Seq[Value] { + return func(yield func(Value) bool) { + if !l.frozen { + l.itercount++ + defer func() { l.itercount-- }() + } + for _, x := range l.elems { + if !yield(x) { + break + } + } + } +} + +// Elements returns a go1.23 iterator over the elements of the tuple. +// +// (A Tuple is a slice, so it is of course directly iterable. This +// method exists to provide a fast path for the [Elements] standalone +// function.) +func (t Tuple) Elements() iter.Seq[Value] { + return func(yield func(Value) bool) { + for _, x := range t { + if !yield(x) { + break + } + } + } +} + +func (s *Set) Elements() iter.Seq[Value] { + return func(yield func(k Value) bool) { + s.ht.entries(func(k, _ Value) bool { return yield(k) }) + } +} + +// Elements returns an iterator for the elements of the iterable value. +// +// Example of go1.23 iteration: +// +// for elem := range Elements(iterable) { ... } +// +// Push iterators are provided as a convenience for Go client code. The +// core iteration behavior of Starlark for-loops is defined by the +// [Iterable] interface. +func Elements(iterable Iterable) iter.Seq[Value] { + // Use specialized push iterator if available (*List, Tuple, *Set). + type hasElements interface { + Elements() iter.Seq[Value] + } + if iterable, ok := iterable.(hasElements); ok { + return iterable.Elements() + } + + iter := iterable.Iterate() + return func(yield func(Value) bool) { + defer iter.Done() + var x Value + for iter.Next(&x) && yield(x) { + } + } +} + +// Entries returns an iterator over the entries (key/value pairs) of +// the iterable mapping. +// +// Example of go1.23 iteration: +// +// for k, v := range Entries(mapping) { ... } +// +// Push iterators are provided as a convenience for Go client code. The +// core iteration behavior of Starlark for-loops is defined by the +// [Iterable] interface. +func Entries(mapping IterableMapping) iter.Seq2[Value, Value] { + // If available (e.g. *Dict), use specialized push iterator, + // as it gets k and v in one shot. + type hasEntries interface { + Entries() iter.Seq2[Value, Value] + } + if mapping, ok := mapping.(hasEntries); ok { + return mapping.Entries() + } + + iter := mapping.Iterate() + return func(yield func(k, v Value) bool) { + defer iter.Done() + var k Value + for iter.Next(&k) { + v, found, err := mapping.Get(k) + if err != nil || !found { + panic(fmt.Sprintf("Iterate and Get are inconsistent (mapping=%v, key=%v)", + mapping.Type(), k.Type())) + } + if !yield(k, v) { + break + } + } + } +} diff --git a/starlark/value.go b/starlark/value.go index d0a9bb35..5e323ca4 100644 --- a/starlark/value.go +++ b/starlark/value.go @@ -863,7 +863,6 @@ func (d *Dict) Type() string { return "dict" func (d *Dict) Freeze() { d.ht.freeze() } func (d *Dict) Truth() Bool { return d.Len() > 0 } func (d *Dict) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: dict") } -func (d *Dict) Entries() func(yield func(k, v Value) bool) { return d.ht.entries } func (x *Dict) Union(y *Dict) *Dict { z := new(Dict) @@ -971,25 +970,6 @@ func (l *List) Iterate() Iterator { return &listIterator{l: l} } -// Elements returns a go1.23 iterator over the elements of the list. -// -// Example: -// -// for elem := range list.Elements() { ... } -func (l *List) Elements() func(yield func(Value) bool) { - return func(yield func(Value) bool) { - if !l.frozen { - l.itercount++ - defer func() { l.itercount-- }() - } - for _, x := range l.elems { - if !yield(x) { - break - } - } - } -} - func (x *List) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(*List) // It's tempting to check x == y as an optimization here, @@ -1090,21 +1070,6 @@ func (t Tuple) Slice(start, end, step int) Value { func (t Tuple) Iterate() Iterator { return &tupleIterator{elems: t} } -// Elements returns a go1.23 iterator over the elements of the tuple. -// -// (A Tuple is a slice, so it is of course directly iterable. This -// method exists to provide a fast path for the [Elements] standalone -// function.) -func (t Tuple) Elements() func(yield func(Value) bool) { - return func(yield func(Value) bool) { - for _, x := range t { - if !yield(x) { - break - } - } - } -} - func (t Tuple) Freeze() { for _, elem := range t { elem.Freeze() @@ -1176,11 +1141,6 @@ func (s *Set) Truth() Bool { return s.Len() > 0 } func (s *Set) Attr(name string) (Value, error) { return builtinAttr(s, name, setMethods) } func (s *Set) AttrNames() []string { return builtinAttrNames(setMethods) } -func (s *Set) Elements() func(yield func(k Value) bool) { - return func(yield func(k Value) bool) { - s.ht.entries(func(k, _ Value) bool { return yield(k) }) - } -} func (x *Set) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(*Set) @@ -1618,74 +1578,6 @@ func Iterate(x Value) Iterator { return nil } -// Elements returns an iterator for the elements of the iterable value. -// -// Example of go1.23 iteration: -// -// for elem := range Elements(iterable) { ... } -// -// Push iterators are provided as a convenience for Go client code. The -// core iteration behavior of Starlark for-loops is defined by the -// [Iterable] interface. -// -// TODO(adonovan): change return type to go1.23 iter.Seq[Value]. -func Elements(iterable Iterable) func(yield func(Value) bool) { - // Use specialized push iterator if available (*List, Tuple, *Set). - type hasElements interface { - Elements() func(yield func(k Value) bool) - } - if iterable, ok := iterable.(hasElements); ok { - return iterable.Elements() - } - - iter := iterable.Iterate() - return func(yield func(Value) bool) { - defer iter.Done() - var x Value - for iter.Next(&x) && yield(x) { - } - } -} - -// Entries returns an iterator over the entries (key/value pairs) of -// the iterable mapping. -// -// Example of go1.23 iteration: -// -// for k, v := range Entries(mapping) { ... } -// -// Push iterators are provided as a convenience for Go client code. The -// core iteration behavior of Starlark for-loops is defined by the -// [Iterable] interface. -// -// TODO(adonovan): change return type to go1.23 iter.Seq2[Value, Value]. -func Entries(mapping IterableMapping) func(yield func(k, v Value) bool) { - // If available (e.g. *Dict), use specialized push iterator, - // as it gets k and v in one shot. - type hasEntries interface { - Entries() func(yield func(k, v Value) bool) - } - if mapping, ok := mapping.(hasEntries); ok { - return mapping.Entries() - } - - iter := mapping.Iterate() - return func(yield func(k, v Value) bool) { - defer iter.Done() - var k Value - for iter.Next(&k) { - v, found, err := mapping.Get(k) - if err != nil || !found { - panic(fmt.Sprintf("Iterate and Get are inconsistent (mapping=%v, key=%v)", - mapping.Type(), k.Type())) - } - if !yield(k, v) { - break - } - } - } -} - // Bytes is the type of a Starlark binary string. // // A Bytes encapsulates an immutable sequence of bytes. diff --git a/syntax/options.go b/syntax/options.go index 51b26382..109aaf63 100644 --- a/syntax/options.go +++ b/syntax/options.go @@ -9,17 +9,17 @@ import _ "unsafe" // for linkname // FileOptions specifies various per-file options that affect static // aspects of an individual file such as parsing, name resolution, and // code generation. (Options that affect global dynamics are typically -// controlled through [starlark.Thread].) +// controlled through [go.starlark.net/starlark.Thread].) // // The zero value of FileOptions is the default behavior. // // Many functions in this package come in two versions: the legacy // standalone function (such as [Parse]) uses [LegacyFileOptions], -// whereas the more recent method (such as [Options.Parse]) honors the +// whereas the more recent method (such as [FileOptions.Parse]) honors the // provided options. The second form is preferred. In other packages, // the modern version is a standalone function with a leading // FileOptions parameter and the name suffix "Options", such as -// [starlark.ExecFileOptions]. +// [go.starlark.net/starlark.ExecFileOptions]. type FileOptions struct { // resolver Set bool // allow references to the 'set' built-in function