Skip to content

Commit dded032

Browse files
authored
Adding new ThreeWayComparable interface (#457)
* Add ThreewayComparable Interface to value.go * Use ThreeWayComparable interface for int type * refactor time.go to use ThreeWayComparable interface * Cleanup * Apply suggestions from review * Apply changes from code reviews
1 parent 50efbbf commit dded032

File tree

3 files changed

+61
-47
lines changed

3 files changed

+61
-47
lines changed

lib/time/time.go

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,15 @@ func (d Duration) AttrNames() []string {
209209
}
210210
}
211211

212-
// CompareSameType implements comparison of two Duration values. required by
213-
// starlark.Comparable interface.
214-
func (d Duration) CompareSameType(op syntax.Token, v starlark.Value, depth int) (bool, error) {
215-
cmp := 0
212+
// Cmp implements comparison of two Duration values. required by
213+
// starlark.TotallyOrdered interface.
214+
func (d Duration) Cmp(v starlark.Value, depth int) (int, error) {
216215
if x, y := d, v.(Duration); x < y {
217-
cmp = -1
216+
return -1, nil
218217
} else if x > y {
219-
cmp = 1
218+
return 1, nil
220219
}
221-
return threeway(op, cmp), nil
220+
return 0, nil
222221
}
223222

224223
// Binary implements binary operators, which satisfies the starlark.HasBinary
@@ -392,18 +391,17 @@ func (t Time) AttrNames() []string {
392391
)
393392
}
394393

395-
// CompareSameType implements comparison of two Time values. required by
396-
// starlark.Comparable interface.
397-
func (t Time) CompareSameType(op syntax.Token, yV starlark.Value, depth int) (bool, error) {
394+
// Cmp implements comparison of two Time values. Required by
395+
// starlark.TotallyOrdered interface.
396+
func (t Time) Cmp(yV starlark.Value, depth int) (int, error) {
398397
x := time.Time(t)
399398
y := time.Time(yV.(Time))
400-
cmp := 0
401399
if x.Before(y) {
402-
cmp = -1
400+
return -1, nil
403401
} else if x.After(y) {
404-
cmp = 1
402+
return 1, nil
405403
}
406-
return threeway(op, cmp), nil
404+
return 0, nil
407405
}
408406

409407
// Binary implements binary operators, which satisfies the starlark.HasBinary
@@ -485,23 +483,3 @@ func builtinAttrNames(methods map[string]builtinMethod) []string {
485483
sort.Strings(names)
486484
return names
487485
}
488-
489-
// Threeway interprets a three-way comparison value cmp (-1, 0, +1)
490-
// as a boolean comparison (e.g. x < y).
491-
func threeway(op syntax.Token, cmp int) bool {
492-
switch op {
493-
case syntax.EQL:
494-
return cmp == 0
495-
case syntax.NEQ:
496-
return cmp != 0
497-
case syntax.LE:
498-
return cmp <= 0
499-
case syntax.LT:
500-
return cmp < 0
501-
case syntax.GE:
502-
return cmp >= 0
503-
case syntax.GT:
504-
return cmp > 0
505-
}
506-
panic(op)
507-
}

starlark/int.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,16 @@ func (i Int) Hash() (uint32, error) {
190190
}
191191
return 12582917 * uint32(lo+3), nil
192192
}
193-
func (x Int) CompareSameType(op syntax.Token, v Value, depth int) (bool, error) {
193+
194+
// Required by the TotallyOrdered interface
195+
func (x Int) Cmp(v Value, depth int) (int, error) {
194196
y := v.(Int)
195197
xSmall, xBig := x.get()
196198
ySmall, yBig := y.get()
197199
if xBig != nil || yBig != nil {
198-
return threeway(op, x.bigInt().Cmp(y.bigInt())), nil
200+
return x.bigInt().Cmp(y.bigInt()), nil
199201
}
200-
return threeway(op, signum64(xSmall-ySmall)), nil
202+
return signum64(xSmall - ySmall), nil // safe: int32 operands
201203
}
202204

203205
// Float returns the float value nearest i.

starlark/value.go

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,41 @@ type Comparable interface {
131131
CompareSameType(op syntax.Token, y Value, depth int) (bool, error)
132132
}
133133

134+
// A TotallyOrdered is a type whose values form a total order:
135+
// if x and y are of the same TotallyOrdered type, then x must be less than y,
136+
// greater than y, or equal to y.
137+
//
138+
// It is simpler than Comparable and should be preferred in new code,
139+
// but if a type implements both interfaces, Comparable takes precedence.
140+
type TotallyOrdered interface {
141+
Value
142+
// Cmp compares two values x and y of the same totally ordered type.
143+
// It returns negative if x < y, positive if x > y, and zero if the values are equal.
144+
//
145+
// Implementations that recursively compare subcomponents of
146+
// the value should use the CompareDepth function, not Cmp, to
147+
// avoid infinite recursion on cyclic structures.
148+
//
149+
// The depth parameter is used to bound comparisons of cyclic
150+
// data structures. Implementations should decrement depth
151+
// before calling CompareDepth and should return an error if depth
152+
// < 1.
153+
//
154+
// Client code should not call this method. Instead, use the
155+
// standalone Compare or Equals functions, which are defined for
156+
// all pairs of operands.
157+
Cmp(y Value, depth int) (int, error)
158+
}
159+
134160
var (
135-
_ Comparable = Int{}
136-
_ Comparable = False
137-
_ Comparable = Float(0)
138-
_ Comparable = String("")
139-
_ Comparable = (*Dict)(nil)
140-
_ Comparable = (*List)(nil)
141-
_ Comparable = Tuple(nil)
142-
_ Comparable = (*Set)(nil)
161+
_ TotallyOrdered = Int{}
162+
_ TotallyOrdered = Float(0)
163+
_ Comparable = False
164+
_ Comparable = String("")
165+
_ Comparable = (*Dict)(nil)
166+
_ Comparable = (*List)(nil)
167+
_ Comparable = Tuple(nil)
168+
_ Comparable = (*Set)(nil)
143169
)
144170

145171
// A Callable value f may be the operand of a function call, f(x).
@@ -439,9 +465,9 @@ func isFinite(f float64) bool {
439465
return math.Abs(f) <= math.MaxFloat64
440466
}
441467

442-
func (x Float) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) {
468+
func (x Float) Cmp(y_ Value, depth int) (int, error) {
443469
y := y_.(Float)
444-
return threeway(op, floatCmp(x, y)), nil
470+
return floatCmp(x, y), nil
445471
}
446472

447473
// floatCmp performs a three-valued comparison on floats,
@@ -1299,6 +1325,14 @@ func CompareDepth(op syntax.Token, x, y Value, depth int) (bool, error) {
12991325
return xcomp.CompareSameType(op, y, depth)
13001326
}
13011327

1328+
if xcomp, ok := x.(TotallyOrdered); ok {
1329+
t, err := xcomp.Cmp(y, depth)
1330+
if err != nil {
1331+
return false, err
1332+
}
1333+
return threeway(op, t), nil
1334+
}
1335+
13021336
// use identity comparison
13031337
switch op {
13041338
case syntax.EQL:

0 commit comments

Comments
 (0)