Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

starlark: require go1.23 for new iterators #551

Merged
merged 1 commit into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions starlark/iter.go
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
108 changes: 0 additions & 108 deletions starlark/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions syntax/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading