mirror of
https://github.com/golang/go
synced 2024-11-22 03:34:40 -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.
|
// are identical in behavior except that 'with' sets dot.
|
||||||
func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) {
|
func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) {
|
||||||
val := s.evalPipeline(data, pipe)
|
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() {
|
switch val.Kind() {
|
||||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
truth = val.Len() > 0
|
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:
|
case reflect.Chan, reflect.Func, reflect.Ptr:
|
||||||
truth = !val.IsNil()
|
truth = !val.IsNil()
|
||||||
default:
|
default:
|
||||||
s.errorf("if/with can't use value of type %T", val.Interface())
|
return
|
||||||
}
|
|
||||||
if truth {
|
|
||||||
if typ == nodeWith {
|
|
||||||
data = val
|
|
||||||
}
|
|
||||||
s.walk(data, list)
|
|
||||||
} else if elseList != nil {
|
|
||||||
s.walk(data, elseList)
|
|
||||||
}
|
}
|
||||||
|
return truth, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) walkRange(data reflect.Value, r *rangeNode) {
|
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 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 emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-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", `{{printf "hello, printf"}}`, "hello, printf", tVal, true},
|
||||||
{"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
|
{"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
|
||||||
{"printf float", `{{printf "%g" 3.5}}`, "3.5", 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 field", `{{printf "%s" .U.V}}`, "v", tVal, true},
|
||||||
{"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", 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},
|
{"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>"}}`,
|
{"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}}`,
|
{"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.
|
||||||
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
|
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
|
||||||
{"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", 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{
|
var funcs = map[string]reflect.Value{
|
||||||
"printf": reflect.ValueOf(fmt.Sprintf),
|
"printf": reflect.ValueOf(fmt.Sprintf),
|
||||||
"html": reflect.ValueOf(HTMLEscaper),
|
"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.
|
// 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
|
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 (
|
var (
|
||||||
escQuot = []byte(""") // shorter than """
|
escQuot = []byte(""") // shorter than """
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -35,6 +36,19 @@ func (s *Set) Funcs(funcMap FuncMap) *Set {
|
|||||||
return s
|
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
|
// recover is the handler that turns panics into returns from the top
|
||||||
// level of Parse.
|
// level of Parse.
|
||||||
func (s *Set) recover(errp *os.Error) {
|
func (s *Set) recover(errp *os.Error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user