From 469e3331069639fd1592593940febe0aacc6d40c Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 14 Jul 2011 07:59:04 +1000 Subject: [PATCH] exp/template: tweak behavior of booleans. Russ suggested this technique, making the "and" and "or" functions handier. But it's hacky, and I can be talked out of it. R=dsymonds, rsc CC=golang-dev https://golang.org/cl/4698044 --- src/pkg/exp/template/doc.go | 10 +++++-- src/pkg/exp/template/exec.go | 2 +- src/pkg/exp/template/exec_test.go | 8 ++++-- src/pkg/exp/template/funcs.go | 43 +++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/pkg/exp/template/doc.go b/src/pkg/exp/template/doc.go index 3764bc70d5..9c439057d1 100644 --- a/src/pkg/exp/template/doc.go +++ b/src/pkg/exp/template/doc.go @@ -199,7 +199,10 @@ the set but the Funcs methods can be used to add them. Predefined global functions are named as follows. and - Returns the boolean AND of its arguments. + Returns the boolean AND of its arguments by returning the + first empty argument or the last argument, that is, + "and x y" behaves as "if x then y else x". All the + arguments are evaluated. html Returns the escaped HTML equivalent of the textual representation of its arguments. @@ -213,7 +216,10 @@ Predefined global functions are named as follows. not Returns the boolean negation of its single argument. or - Returns the boolean OR of its arguments. + Returns the boolean OR of its arguments by returning the + first non-empty argument or the last argument, that is, + "or x y" behaves as "if x then x else y". All the + arguments are evaluated. print An alias for fmt.Sprint printf diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go index 6955809308..b00f6a7582 100644 --- a/src/pkg/exp/template/exec.go +++ b/src/pkg/exp/template/exec.go @@ -289,7 +289,7 @@ func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.V case *stringNode: return reflect.ValueOf(word.text) } - s.errorf("can't handle command %q", firstWord) + s.errorf("can't evaluate command %q", firstWord) panic("not reached") } diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go index fc77c48e95..7e0301c8d8 100644 --- a/src/pkg/exp/template/exec_test.go +++ b/src/pkg/exp/template/exec_test.go @@ -280,8 +280,8 @@ var execTests = []execTest{ // Booleans {"not", "{{not true}} {{not false}}", "false true", nil, true}, - {"and", "{{and 0 0}} {{and 1 0}} {{and 0 1}} {{and 1 1}}", "false false false true", nil, true}, - {"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true}, + {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, + {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, @@ -326,6 +326,10 @@ var execTests = []execTest{ {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, + // Cute examples. + {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, + {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, + // Error handling. {"error method, error", "{{.EPERM true}}", "", tVal, false}, {"error method, no error", "{{.EPERM false}}", "false", tVal, true}, diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go index 3aa9d629a8..3bf2bdd636 100644 --- a/src/pkg/exp/template/funcs.go +++ b/src/pkg/exp/template/funcs.go @@ -122,22 +122,39 @@ func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { // Boolean logic. -// and returns the Boolean AND of its arguments. -func and(arg0 interface{}, args ...interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg0)) - for i := 0; truth && i < len(args); i++ { - truth, _ = isTrue(reflect.ValueOf(args[i])) - } - return +func truth(a interface{}) bool { + t, _ := isTrue(reflect.ValueOf(a)) + return t } -// or returns the Boolean OR of its arguments. -func or(arg0 interface{}, args ...interface{}) (truth bool) { - truth, _ = isTrue(reflect.ValueOf(arg0)) - for i := 0; !truth && i < len(args); i++ { - truth, _ = isTrue(reflect.ValueOf(args[i])) +// and computes the Boolean AND of its arguments, returning +// the first false argument it encounters, or the last argument. +func and(arg0 interface{}, args ...interface{}) interface{} { + if !truth(arg0) { + return arg0 } - return + for i := range args { + arg0 = args[i] + if !truth(arg0) { + break + } + } + return arg0 +} + +// or computes the Boolean OR of its arguments, returning +// the first true argument it encounters, or the last argument. +func or(arg0 interface{}, args ...interface{}) interface{} { + if truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if truth(arg0) { + break + } + } + return arg0 } // not returns the Boolean negation of its argument.