Commit 469e3331 authored by Rob Pike's avatar Rob Pike

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
parent c3344d61
...@@ -199,7 +199,10 @@ the set but the Funcs methods can be used to add them. ...@@ -199,7 +199,10 @@ the set but the Funcs methods can be used to add them.
Predefined global functions are named as follows. Predefined global functions are named as follows.
and 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 html
Returns the escaped HTML equivalent of the textual Returns the escaped HTML equivalent of the textual
representation of its arguments. representation of its arguments.
...@@ -213,7 +216,10 @@ Predefined global functions are named as follows. ...@@ -213,7 +216,10 @@ Predefined global functions are named as follows.
not not
Returns the boolean negation of its single argument. Returns the boolean negation of its single argument.
or 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 print
An alias for fmt.Sprint An alias for fmt.Sprint
printf printf
......
...@@ -289,7 +289,7 @@ func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.V ...@@ -289,7 +289,7 @@ func (s *state) evalCommand(dot reflect.Value, cmd *commandNode, final reflect.V
case *stringNode: case *stringNode:
return reflect.ValueOf(word.text) return reflect.ValueOf(word.text)
} }
s.errorf("can't handle command %q", firstWord) s.errorf("can't evaluate command %q", firstWord)
panic("not reached") panic("not reached")
} }
......
...@@ -280,8 +280,8 @@ var execTests = []execTest{ ...@@ -280,8 +280,8 @@ var execTests = []execTest{
// Booleans // Booleans
{"not", "{{not true}} {{not false}}", "false true", nil, true}, {"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}, {"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 1}} {{or 1 1}}", "false true true true", 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", "{{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}, {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true},
...@@ -326,6 +326,10 @@ var execTests = []execTest{ ...@@ -326,6 +326,10 @@ var execTests = []execTest{
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
{"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", 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 handling.
{"error method, error", "{{.EPERM true}}", "", tVal, false}, {"error method, error", "{{.EPERM true}}", "", tVal, false},
{"error method, no error", "{{.EPERM false}}", "false", tVal, true}, {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
......
...@@ -122,22 +122,39 @@ func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { ...@@ -122,22 +122,39 @@ func index(item interface{}, indices ...interface{}) (interface{}, os.Error) {
// Boolean logic. // Boolean logic.
// and returns the Boolean AND of its arguments. func truth(a interface{}) bool {
func and(arg0 interface{}, args ...interface{}) (truth bool) { t, _ := isTrue(reflect.ValueOf(a))
truth, _ = isTrue(reflect.ValueOf(arg0)) return t
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
}
for i := range args {
arg0 = args[i]
if !truth(arg0) {
break
}
} }
return return arg0
} }
// or returns the Boolean OR of its arguments. // or computes the Boolean OR of its arguments, returning
func or(arg0 interface{}, args ...interface{}) (truth bool) { // the first true argument it encounters, or the last argument.
truth, _ = isTrue(reflect.ValueOf(arg0)) func or(arg0 interface{}, args ...interface{}) interface{} {
for i := 0; !truth && i < len(args); i++ { if truth(arg0) {
truth, _ = isTrue(reflect.ValueOf(args[i])) return arg0
}
for i := range args {
arg0 = args[i]
if truth(arg0) {
break
}
} }
return return arg0
} }
// not returns the Boolean negation of its argument. // not returns the Boolean negation of its argument.
......
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