mirror of
https://github.com/golang/go
synced 2024-11-24 21:10:04 -07:00
exp/template: add templates to sets; boolean logic.
R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/4670045
This commit is contained in:
parent
b7db4fef22
commit
eea5443572
@ -89,7 +89,23 @@ func (s *state) walk(data reflect.Value, n node) {
|
||||
// are identical in behavior except that 'with' sets dot.
|
||||
func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) {
|
||||
val := s.evalPipeline(data, pipe)
|
||||
truth := false
|
||||
truth, ok := isTrue(val)
|
||||
if !ok {
|
||||
s.errorf("if/with can't use value of type %T", val.Interface())
|
||||
}
|
||||
if truth {
|
||||
if typ == nodeWith {
|
||||
data = val
|
||||
}
|
||||
s.walk(data, list)
|
||||
} else if elseList != nil {
|
||||
s.walk(data, elseList)
|
||||
}
|
||||
}
|
||||
|
||||
// isTrue returns whether the value is 'true', in the sense of not the zero of its type,
|
||||
// and whether the value has a meaningful truth value.
|
||||
func isTrue(val reflect.Value) (truth, ok bool) {
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
truth = val.Len() > 0
|
||||
@ -106,16 +122,9 @@ func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNo
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr:
|
||||
truth = !val.IsNil()
|
||||
default:
|
||||
s.errorf("if/with can't use value of type %T", val.Interface())
|
||||
}
|
||||
if truth {
|
||||
if typ == nodeWith {
|
||||
data = val
|
||||
}
|
||||
s.walk(data, list)
|
||||
} else if elseList != nil {
|
||||
s.walk(data, elseList)
|
||||
return
|
||||
}
|
||||
return truth, true
|
||||
}
|
||||
|
||||
func (s *state) walkRange(data reflect.Value, r *rangeNode) {
|
||||
|
@ -140,7 +140,7 @@ var execTests = []execTest{
|
||||
{"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
||||
{"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||
{"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
|
||||
// Function calls.
|
||||
// Printf.
|
||||
{"printf", `{{printf "hello, printf"}}`, "hello, printf", tVal, true},
|
||||
{"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
|
||||
{"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
|
||||
@ -150,10 +150,17 @@ var execTests = []execTest{
|
||||
{"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
|
||||
{"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true},
|
||||
{"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true},
|
||||
// HTML.
|
||||
{"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
|
||||
"<script>alert("XSS");</script>", tVal, true},
|
||||
"<script>alert("XSS");</script>", nil, true},
|
||||
{"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
|
||||
"<script>alert("XSS");</script>", tVal, true},
|
||||
"<script>alert("XSS");</script>", nil, true},
|
||||
// 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},
|
||||
{"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},
|
||||
// With.
|
||||
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
|
||||
{"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
|
||||
|
@ -20,6 +20,9 @@ type FuncMap map[string]interface{}
|
||||
var funcs = map[string]reflect.Value{
|
||||
"printf": reflect.ValueOf(fmt.Sprintf),
|
||||
"html": reflect.ValueOf(HTMLEscaper),
|
||||
"and": reflect.ValueOf(and),
|
||||
"or": reflect.ValueOf(or),
|
||||
"not": reflect.ValueOf(not),
|
||||
}
|
||||
|
||||
// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
||||
@ -66,7 +69,33 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
// HTML escaping
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// not returns the Boolean negation of its argument.
|
||||
func not(arg interface{}) (truth bool) {
|
||||
truth, _ = isTrue(reflect.ValueOf(arg))
|
||||
return !truth
|
||||
}
|
||||
|
||||
// HTML escaping.
|
||||
|
||||
var (
|
||||
escQuot = []byte(""") // shorter than """
|
||||
|
@ -5,6 +5,7 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@ -35,6 +36,19 @@ func (s *Set) Funcs(funcMap FuncMap) *Set {
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds the argument templates to the set. It panics if the call
|
||||
// attempts to reuse a name defined in the template.
|
||||
// The return value is the set, so calls can be chained.
|
||||
func (s *Set) Add(templates ...*Template) *Set {
|
||||
for _, t := range templates {
|
||||
if _, ok := s.tmpl[t.name]; ok {
|
||||
panic(fmt.Errorf("template: %q already defined in set", t.name))
|
||||
}
|
||||
s.tmpl[t.name] = t
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// recover is the handler that turns panics into returns from the top
|
||||
// level of Parse.
|
||||
func (s *Set) recover(errp *os.Error) {
|
||||
|
Loading…
Reference in New Issue
Block a user