Commit 0ba7ffe2 authored by Rob Pike's avatar Rob Pike

text/template: allow eq to take more than two arguments

Based on an old suggestion by rsc, it compares the second
and following arguments to the first.

Unfortunately the code cannot be as pretty as rsc's original
because it doesn't require identical types.

R=golang-dev, dsymonds, adg
CC=golang-dev
https://golang.org/cl/13509046
parent 54b2a83d
...@@ -326,13 +326,22 @@ functions: ...@@ -326,13 +326,22 @@ functions:
ge ge
Returns the boolean truth of arg1 >= arg2 Returns the boolean truth of arg1 >= arg2
These functions work on basic types only (or named basic types, For simpler multi-way equality tests, eq (only) accepts two or more
such as "type Celsius float32"). They implement the Go rules for arguments and compares the second and subsequent to the first,
comparison of values, except that size and exact type are ignored, returning in effect
so any integer value may be compared with any other integer value,
any unsigned integer value may be compared with any other unsigned arg1==arg2 || arg1==arg3 || arg1==arg4 ...
integer value, and so on. However, as usual, one may not compare
an int with a float32 and so on. (Unlike with || in Go, however, eq is a function call and all the
arguments will be evaluated.)
The comparison functions work on basic types only (or named basic
types, such as "type Celsius float32"). They implement the Go rules
for comparison of values, except that size and exact type are
ignored, so any integer value may be compared with any other integer
value, any unsigned integer value may be compared with any other
unsigned integer value, and so on. However, as usual, one may not
compare an int with a float32 and so on.
Associated templates Associated templates
......
...@@ -891,6 +891,8 @@ var cmpTests = []cmpTest{ ...@@ -891,6 +891,8 @@ var cmpTests = []cmpTest{
{"eq `xy` `xyz`", "false", true}, {"eq `xy` `xyz`", "false", true},
{"eq .Xuint .Xuint", "true", true}, {"eq .Xuint .Xuint", "true", true},
{"eq .Xuint .Yuint", "false", true}, {"eq .Xuint .Yuint", "false", true},
{"eq 3 4 5 6 3", "true", true},
{"eq 3 4 5 6 7", "false", true},
{"ne true true", "false", true}, {"ne true true", "false", true},
{"ne true false", "true", true}, {"ne true false", "true", true},
{"ne 1+2i 1+2i", "false", true}, {"ne 1+2i 1+2i", "false", true},
...@@ -946,7 +948,6 @@ var cmpTests = []cmpTest{ ...@@ -946,7 +948,6 @@ var cmpTests = []cmpTest{
{"ge .Xuint .Yuint", "false", true}, {"ge .Xuint .Yuint", "false", true},
{"ge .Yuint .Xuint", "true", true}, {"ge .Yuint .Xuint", "true", true},
// Errors // Errors
{"eq 3 4 5", "", false}, // Too many arguments.
{"eq `xy` 1", "", false}, // Different types. {"eq `xy` 1", "", false}, // Different types.
{"lt true true", "", false}, // Unordered types. {"lt true true", "", false}, // Unordered types.
{"lt 1+0i 1+0i", "", false}, // Unordered types. {"lt 1+0i 1+0i", "", false}, // Unordered types.
...@@ -970,7 +971,7 @@ func TestComparison(t *testing.T) { ...@@ -970,7 +971,7 @@ func TestComparison(t *testing.T) {
continue continue
} }
if !test.ok && err == nil { if !test.ok && err == nil {
t.Errorf("%s did not error") t.Errorf("%s did not error", test.expr)
continue continue
} }
if b.String() != test.truth { if b.String() != test.truth {
......
...@@ -264,6 +264,7 @@ func not(arg interface{}) (truth bool) { ...@@ -264,6 +264,7 @@ func not(arg interface{}) (truth bool) {
var ( var (
errBadComparisonType = errors.New("invalid type for comparison") errBadComparisonType = errors.New("invalid type for comparison")
errBadComparison = errors.New("incompatible types for comparison") errBadComparison = errors.New("incompatible types for comparison")
errNoComparison = errors.New("missing argument for comparison")
) )
type kind int type kind int
...@@ -297,39 +298,47 @@ func basicKind(v reflect.Value) (kind, error) { ...@@ -297,39 +298,47 @@ func basicKind(v reflect.Value) (kind, error) {
return invalidKind, errBadComparisonType return invalidKind, errBadComparisonType
} }
// eq evaluates the comparison a == b. // eq evaluates the comparison a == b || a == c || ...
func eq(arg1, arg2 interface{}) (bool, error) { func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
v1 := reflect.ValueOf(arg1) v1 := reflect.ValueOf(arg1)
k1, err := basicKind(v1) k1, err := basicKind(v1)
if err != nil { if err != nil {
return false, err return false, err
} }
v2 := reflect.ValueOf(arg2) if len(arg2) == 0 {
k2, err := basicKind(v2) return false, errNoComparison
if err != nil {
return false, err
} }
if k1 != k2 { for _, arg := range arg2 {
return false, errBadComparison v2 := reflect.ValueOf(arg)
} k2, err := basicKind(v2)
truth := false if err != nil {
switch k1 { return false, err
case boolKind: }
truth = v1.Bool() == v2.Bool() if k1 != k2 {
case complexKind: return false, errBadComparison
truth = v1.Complex() == v2.Complex() }
case floatKind: truth := false
truth = v1.Float() == v2.Float() switch k1 {
case intKind: case boolKind:
truth = v1.Int() == v2.Int() truth = v1.Bool() == v2.Bool()
case stringKind: case complexKind:
truth = v1.String() == v2.String() truth = v1.Complex() == v2.Complex()
case uintKind: case floatKind:
truth = v1.Uint() == v2.Uint() truth = v1.Float() == v2.Float()
default: case intKind:
panic("invalid kind") truth = v1.Int() == v2.Int()
case stringKind:
truth = v1.String() == v2.String()
case uintKind:
truth = v1.Uint() == v2.Uint()
default:
panic("invalid kind")
}
if truth {
return true, nil
}
} }
return truth, nil return false, nil
} }
// ne evaluates the comparison a != b. // ne evaluates the comparison a != b.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment