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

Adding additional set builtins #506

Merged
merged 8 commits into from
Sep 12, 2023
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
80 changes: 80 additions & 0 deletions doc/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,14 @@ reproducibility is paramount, such as build tools.
* [list·remove](#list·remove)
* [set·add](#set·add)
* [set·clear](#set·clear)
* [set·difference](#set·difference)
* [set·discard](#set·discard)
* [set·intersection](#set·intersection)
* [set·issubset](#set·issubset)
* [set·issuperset](#set·issuperset)
* [set·pop](#set·pop)
* [set·remove](#set·remove)
* [set·symmetric_difference](#set·symmetric_difference)
* [set·union](#set·union)
* [string·capitalize](#string·capitalize)
* [string·codepoint_ords](#string·codepoint_ords)
Expand Down Expand Up @@ -975,9 +980,14 @@ A set has these methods:

* [`add`](#set·add)
* [`clear`](#set·clear)
* [`difference`](#set·difference)
* [`discard`](#set·discard)
* [`intersection`](#set·intersection)
* [`issubset`](#set·issubset)
* [`issuperset`](#set·issuperset)
* [`pop`](#set·pop)
* [`remove`](#set·remove)
* [`symmetric_difference`](#set·symmetric_difference)
* [`union`](#set·union)


Expand Down Expand Up @@ -1995,6 +2005,11 @@ which breaks several mathematical identities. For example, if `x` is
a `NaN` value, the comparisons `x < y`, `x == y`, and `x > y` all
yield false for all values of `y`.

When used to compare two `set` objects, the `<=`, and `>=` operators will report
whether one set is a subset or superset of another. Similarly, using `<` or `>` will
report whether a set is a proper subset or superset of another, thus `x > y` is
equivalent to `x >= y and x != y`.

Applications may define additional types that support ordered
comparison.

Expand Down Expand Up @@ -2045,6 +2060,8 @@ Sets
int & int # bitwise intersection (AND)
set & set # set intersection
set ^ set # set symmetric difference
set - set # set difference


Dict
dict | dict # ordered union
Expand Down Expand Up @@ -2115,6 +2132,7 @@ Implementations may impose a limit on the second operand of a left shift.
set([1, 2]) & set([2, 3]) # set([2])
set([1, 2]) | set([2, 3]) # set([1, 2, 3])
set([1, 2]) ^ set([2, 3]) # set([1, 3])
set([1, 2]) - set([2, 3]) # set([1])
```

<b>Implementation note:</b>
Expand Down Expand Up @@ -3782,6 +3800,18 @@ x.clear(2) # None
x # set([])
```

<a id='set·difference'></a>
### set·difference

`S.difference(y)` returns a new set into which have been inserted all the elements of set S which are not in y.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This phrasing nicely finesses both the question of content and of ordering.


y can be any type of iterable (e.g. set, list, tuple).

```python
x = set([1, 2, 3])
x.difference([3, 4, 5]) # set([1, 2])
```

<a id='set·discard'></a>
### set·discard

Expand All @@ -3798,6 +3828,44 @@ x.discard(2) # None
x # set([1, 3])
```

<a id='set·intersection'></a>
### set·intersection

`S.intersection(y)` returns a new set into which have been inserted all the elements of set S which are also in y.

y can be any type of iterable (e.g. set, list, tuple).

```python
x = set([1, 2, 3])
x.intersection([3, 4, 5]) # set([3])
```

<a id='set·issubset'></a>
### set·issubset

`S.issubset(y)` returns True if all items in S are also in y, otherwise it returns False.

y can be any type of iterable (e.g. set, list, tuple).

```python
x = set([1, 2])
x.issubset([1, 2, 3]) # True
x.issubset([1, 3, 4]) # False
```

<a id='set·issuperset'></a>
### set·issuperset

`S.issuperset(y)` returns True if all items in y are also in S, otherwise it returns False.

y can be any type of iterable (e.g. set, list, tuple).

```python
x = set([1, 2, 3])
x.issuperset([1, 2]) # True
x.issuperset([1, 3, 4]) # False
```

<a id='set·pop'></a>
### set·pop

Expand Down Expand Up @@ -3826,6 +3894,18 @@ x # set([1, 3])
x.remove(2) # error: element not found
```

<a id='set·symmetric_difference'></a>
### set·symmetric_difference

`S.symmetric_difference(y)` creates a new set into which is inserted all of the items which are in S but not y, followed by all of the items which are in y but not S.

y can be any type of iterable (e.g. set, list, tuple).

```python
x = set([1, 2, 3])
x.symmetric_difference([3, 4, 5]) # set([1, 2, 4, 5])
```

<a id='set·union'></a>
### set·union

Expand Down
35 changes: 12 additions & 23 deletions starlark/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,12 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
}
return x - yf, nil
}
case *Set: // difference
if y, ok := y.(*Set); ok {
iter := y.Iterate()
defer iter.Done()
return x.Difference(iter)
}
}

case syntax.STAR:
Expand Down Expand Up @@ -1097,17 +1103,9 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
}
case *Set: // intersection
if y, ok := y.(*Set); ok {
set := new(Set)
if x.Len() > y.Len() {
x, y = y, x // opt: range over smaller set
}
for xe := x.ht.head; xe != nil; xe = xe.next {
// Has, Insert cannot fail here.
if found, _ := y.Has(xe.key); found {
set.Insert(xe.key)
}
}
return set, nil
iter := y.Iterate()
defer iter.Done()
return x.Intersection(iter)
}
}

Expand All @@ -1119,18 +1117,9 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
}
case *Set: // symmetric difference
if y, ok := y.(*Set); ok {
set := new(Set)
for xe := x.ht.head; xe != nil; xe = xe.next {
if found, _ := y.Has(xe.key); !found {
set.Insert(xe.key)
}
}
for ye := y.ht.head; ye != nil; ye = ye.next {
if found, _ := x.Has(ye.key); !found {
set.Insert(ye.key)
}
}
return set, nil
iter := y.Iterate()
defer iter.Done()
return x.SymmetricDifference(iter)
}
}

Expand Down
94 changes: 88 additions & 6 deletions starlark/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,17 @@ var (
}

setMethods = map[string]*Builtin{
"add": NewBuiltin("add", set_add),
"clear": NewBuiltin("clear", set_clear),
"discard": NewBuiltin("discard", set_discard),
"pop": NewBuiltin("pop", set_pop),
"remove": NewBuiltin("remove", set_remove),
"union": NewBuiltin("union", set_union),
"add": NewBuiltin("add", set_add),
"clear": NewBuiltin("clear", set_clear),
"difference": NewBuiltin("difference", set_difference),
"discard": NewBuiltin("discard", set_discard),
"intersection": NewBuiltin("intersection", set_intersection),
"issubset": NewBuiltin("issubset", set_issubset),
"issuperset": NewBuiltin("issuperset", set_issuperset),
"pop": NewBuiltin("pop", set_pop),
"remove": NewBuiltin("remove", set_remove),
"symmetric_difference": NewBuiltin("symmetric_difference", set_symmetric_difference),
"union": NewBuiltin("union", set_union),
}
)

Expand Down Expand Up @@ -2204,6 +2209,68 @@ func set_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error)
return None, nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·difference.
func set_difference(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
// TODO: support multiple others: s.difference(*others)
var other Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &other); err != nil {
return nil, err
}
iter := other.Iterate()
defer iter.Done()
diff, err := b.Receiver().(*Set).Difference(iter)
if err != nil {
return nil, nameErr(b, err)
}
return diff, nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set_intersection.
func set_intersection(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
// TODO: support multiple others: s.difference(*others)
var other Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &other); err != nil {
return nil, err
}
iter := other.Iterate()
defer iter.Done()
diff, err := b.Receiver().(*Set).Intersection(iter)
if err != nil {
return nil, nameErr(b, err)
}
return diff, nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set_issubset.
func set_issubset(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var other Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &other); err != nil {
return nil, err
}
iter := other.Iterate()
defer iter.Done()
diff, err := b.Receiver().(*Set).IsSubset(iter)
if err != nil {
return nil, nameErr(b, err)
}
return Bool(diff), nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set_issuperset.
func set_issuperset(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var other Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &other); err != nil {
return nil, err
}
iter := other.Iterate()
defer iter.Done()
diff, err := b.Receiver().(*Set).IsSuperset(iter)
if err != nil {
return nil, nameErr(b, err)
}
return Bool(diff), nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·discard.
func set_discard(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var k Value
Expand Down Expand Up @@ -2252,6 +2319,21 @@ func set_remove(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error
return nil, nameErr(b, "missing key")
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·symmetric_difference.
func set_symmetric_difference(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var other Iterable
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &other); err != nil {
return nil, err
}
iter := other.Iterate()
defer iter.Done()
diff, err := b.Receiver().(*Set).SymmetricDifference(iter)
if err != nil {
return nil, nameErr(b, err)
}
return diff, nil
}

// https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union.
func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
var iterable Iterable
Expand Down
2 changes: 1 addition & 1 deletion starlark/testdata/builtins.star
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ assert.eq(getattr(hf, "x"), 2)
assert.eq(hf.x, 2)
# built-in types can have attributes (methods) too.
myset = set([])
assert.eq(dir(myset), ["add", "clear", "discard", "pop", "remove", "union"])
assert.eq(dir(myset), ["add", "clear", "difference", "discard", "intersection", "issubset", "issuperset", "pop", "remove", "symmetric_difference", "union"])
assert.true(hasattr(myset, "union"))
assert.true(not hasattr(myset, "onion"))
assert.eq(str(getattr(myset, "union")), "<built-in method union of set value>")
Expand Down
2 changes: 0 additions & 2 deletions starlark/testdata/int.star
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def compound():
x %= 3
assert.eq(x, 2)

# use resolve.AllowBitwise to enable the ops:
x = 2
x &= 1
assert.eq(x, 0)
Expand Down Expand Up @@ -197,7 +196,6 @@ assert.fails(lambda: int("0x-4", 16), "invalid literal with base 16: 0x-4")

# bitwise union (int|int), intersection (int&int), XOR (int^int), unary not (~int),
# left shift (int<<int), and right shift (int>>int).
# use resolve.AllowBitwise to enable the ops.
# TODO(adonovan): this is not yet in the Starlark spec,
# but there is consensus that it should be.
assert.eq(1 | 2, 3)
Expand Down
Loading
Loading