aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/text
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-03-06 17:57:23 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-03-06 17:57:23 +0000
commit593f74bbab63d34c7060918088bcbad686c31c66 (patch)
tree4ce83ca433796a728e9fdd00af105bce158532b5 /libgo/go/text
parent46402cbe0ba3ea92be9642cf18eedaefe57a414c (diff)
downloadgcc-593f74bbab63d34c7060918088bcbad686c31c66.zip
gcc-593f74bbab63d34c7060918088bcbad686c31c66.tar.gz
gcc-593f74bbab63d34c7060918088bcbad686c31c66.tar.bz2
libgo: Update to weekly.2012-03-04 release.
From-SVN: r185010
Diffstat (limited to 'libgo/go/text')
-rw-r--r--libgo/go/text/template/doc.go23
-rw-r--r--libgo/go/text/template/exec.go5
-rw-r--r--libgo/go/text/template/exec_test.go29
-rw-r--r--libgo/go/text/template/funcs.go48
-rw-r--r--libgo/go/text/template/multi_test.go6
-rw-r--r--libgo/go/text/template/parse/parse.go2
-rw-r--r--libgo/go/text/template/parse/parse_test.go3
-rw-r--r--libgo/go/text/template/template.go20
8 files changed, 109 insertions, 27 deletions
diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go
index ae91f4a..10e0f7f 100644
--- a/libgo/go/text/template/doc.go
+++ b/libgo/go/text/template/doc.go
@@ -142,11 +142,6 @@ An argument is a simple value, denoted by one of the following.
.Field1.Key1.Method1.Field2.Key2.Method2
Methods can also be evaluated on variables, including chaining:
$x.Method1.Field
- - The name of a niladic function-valued struct field of the data,
- preceded by a period, such as
- .Function
- Function-valued fields behave like methods (of structs) but do not
- pass a receiver.
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun(). The return
@@ -155,6 +150,10 @@ An argument is a simple value, denoted by one of the following.
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required.
+If an evaluation yields a function value, such as a function-valued
+field of a struct, the function is not invoked automatically, but it
+can be used as a truth value for an if action and the like. To invoke
+it, use the call function, defined below.
A pipeline is a possibly chained sequence of "commands". A command is a simple
value (argument) or a function or method call, possibly with multiple arguments:
@@ -167,9 +166,6 @@ value (argument) or a function or method call, possibly with multiple arguments:
The result is the value of calling the method with the
arguments:
dot.Method(Argument1, etc.)
- .Function [Argument...]
- A function-valued field of a struct works like a method but does
- not pass the receiver.
functionName [Argument...]
The result is the value of calling the function associated
with the name:
@@ -257,6 +253,17 @@ Predefined global functions are named as follows.
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.
+ call
+ Returns the result of calling the first argument, which
+ must be a function, with the remaining arguments as parameters.
+ Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
+ Y is a func-valued field, map entry, or the like.
+ The first argument must be the result of an evaluation
+ that yields a value of function type (as distinct from
+ a predefined function such as print). The function must
+ return either one or two result values, the second of which
+ is of type error. If the arguments don't match the function
+ or the returned error value is non-nil, execution stops.
html
Returns the escaped HTML equivalent of the textual
representation of its arguments.
diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go
index af74528..ad0118e 100644
--- a/libgo/go/text/template/exec.go
+++ b/libgo/go/text/template/exec.go
@@ -421,11 +421,8 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
field := receiver.FieldByIndex(tField.Index)
if tField.PkgPath == "" { // field is exported
// If it's a function, we must call it.
- if field.Type().Kind() == reflect.Func {
- return s.evalCall(dot, field, fieldName, args, final)
- }
if hasArgs {
- s.errorf("%s is not a method or function but has arguments", fieldName)
+ s.errorf("%s has arguments but cannot be invoked as function", fieldName)
}
return field
}
diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go
index 159cf51..70ab39c 100644
--- a/libgo/go/text/template/exec_test.go
+++ b/libgo/go/text/template/exec_test.go
@@ -60,7 +60,9 @@ type T struct {
PSI *[]int
NIL *int
// Function (not method)
- Func func(...string) string
+ BinaryFunc func(string, string) string
+ VariadicFunc func(...string) string
+ VariadicFuncInt func(int, ...string) string
// Template to test evaluation of templates.
Tmpl *Template
}
@@ -120,7 +122,9 @@ var tVal = &T{
Err: errors.New("erroozle"),
PI: newInt(23),
PSI: newIntSlice(21, 22, 23),
- Func: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
+ VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
+ VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
@@ -300,13 +304,26 @@ var execTests = []execTest{
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
"true", tVal, true},
- // Function call
- {".Func", "-{{.Func}}-", "-<>-", tVal, true},
- {".Func2", "-{{.Func `he` `llo`}}-", "-<he+llo>-", tVal, true},
+ // Function call builtin.
+ {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
+ {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true},
+ {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "<he+llo>", tVal, true},
+ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
+ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
+ {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
+
+ // Erroneous function calls (check args).
+ {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
+ {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false},
+ {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false},
+ {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false},
+ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
+ {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
+ {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
// Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
- {"pipeline func", "-{{.Func `llo` | .Func `he` }}-", "-<he+<llo>>-", tVal, true},
+ {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true},
// If.
{"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go
index d6e4bf1..525179c 100644
--- a/libgo/go/text/template/funcs.go
+++ b/libgo/go/text/template/funcs.go
@@ -24,6 +24,7 @@ type FuncMap map[string]interface{}
var builtins = FuncMap{
"and": and,
+ "call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
@@ -151,6 +152,53 @@ func length(item interface{}) (int, error) {
return 0, fmt.Errorf("len of type %s", v.Type())
}
+// Function invocation
+
+// call returns the result of evaluating the the first argument as a function.
+// The function must return 1 result, or 2 results, the second of which is an error.
+func call(fn interface{}, args ...interface{}) (interface{}, error) {
+ v := reflect.ValueOf(fn)
+ typ := v.Type()
+ if typ.Kind() != reflect.Func {
+ return nil, fmt.Errorf("non-function of type %s", typ)
+ }
+ if !goodFunc(typ) {
+ return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
+ }
+ numIn := typ.NumIn()
+ var dddType reflect.Type
+ if typ.IsVariadic() {
+ if len(args) < numIn-1 {
+ return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
+ }
+ dddType = typ.In(numIn - 1).Elem()
+ } else {
+ if len(args) != numIn {
+ return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
+ }
+ }
+ argv := make([]reflect.Value, len(args))
+ for i, arg := range args {
+ value := reflect.ValueOf(arg)
+ // Compute the expected type. Clumsy because of variadics.
+ var argType reflect.Type
+ if !typ.IsVariadic() || i < numIn-1 {
+ argType = typ.In(i)
+ } else {
+ argType = dddType
+ }
+ if !value.Type().AssignableTo(argType) {
+ return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
+ }
+ argv[i] = reflect.ValueOf(arg)
+ }
+ result := v.Call(argv)
+ if len(result) == 2 {
+ return result[0].Interface(), result[1].Interface().(error)
+ }
+ return result[0].Interface(), nil
+}
+
// Boolean logic.
func truth(a interface{}) bool {
diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go
index f205e6b..22dedc4 100644
--- a/libgo/go/text/template/multi_test.go
+++ b/libgo/go/text/template/multi_test.go
@@ -265,6 +265,12 @@ func TestRedefinition(t *testing.T) {
if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
t.Fatalf("parse 1: %v", err)
}
+ if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil {
+ t.Fatal("expected error")
+ }
+ if !strings.Contains(err.Error(), "redefinition") {
+ t.Fatalf("expected redefinition error; got %v", err)
+ }
if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil {
t.Fatal("expected error")
}
diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go
index 35194f7..d67b388 100644
--- a/libgo/go/text/template/parse/parse.go
+++ b/libgo/go/text/template/parse/parse.go
@@ -193,6 +193,8 @@ func (t *Tree) add(treeSet map[string]*Tree) {
// IsEmptyTree reports whether this tree (node) is empty of everything but space.
func IsEmptyTree(n Node) bool {
switch n := n.(type) {
+ case nil:
+ return true
case *ActionNode:
case *IfNode:
case *ListNode:
diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go
index efa7d8b..18c0a8b 100644
--- a/libgo/go/text/template/parse/parse_test.go
+++ b/libgo/go/text/template/parse/parse_test.go
@@ -287,6 +287,9 @@ var isEmptyTests = []isEmptyTest{
}
func TestIsEmpty(t *testing.T) {
+ if !IsEmptyTree(nil) {
+ t.Errorf("nil tree is not empty")
+ }
for _, test := range isEmptyTests {
tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil)
if err != nil {
diff --git a/libgo/go/text/template/template.go b/libgo/go/text/template/template.go
index 7494f9d..82fc9e5 100644
--- a/libgo/go/text/template/template.go
+++ b/libgo/go/text/template/template.go
@@ -178,10 +178,11 @@ func (t *Template) Parse(text string) (*Template, error) {
tmpl = t.New(name)
}
// Even if t == tmpl, we need to install it in the common.tmpl map.
- if err := t.associate(tmpl); err != nil {
+ if replace, err := t.associate(tmpl, tree); err != nil {
return nil, err
+ } else if replace {
+ tmpl.Tree = tree
}
- tmpl.Tree = tree
tmpl.leftDelim = t.leftDelim
tmpl.rightDelim = t.rightDelim
}
@@ -191,22 +192,23 @@ func (t *Template) Parse(text string) (*Template, error) {
// associate installs the new template into the group of templates associated
// with t. It is an error to reuse a name except to overwrite an empty
// template. The two are already known to share the common structure.
-func (t *Template) associate(new *Template) error {
+// The boolean return value reports wither to store this tree as t.Tree.
+func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
if new.common != t.common {
panic("internal error: associate not common")
}
name := new.name
if old := t.tmpl[name]; old != nil {
oldIsEmpty := parse.IsEmptyTree(old.Root)
- newIsEmpty := new.Tree != nil && parse.IsEmptyTree(new.Root)
- if !oldIsEmpty && !newIsEmpty {
- return fmt.Errorf("template: redefinition of template %q", name)
- }
+ newIsEmpty := parse.IsEmptyTree(tree.Root)
if newIsEmpty {
// Whether old is empty or not, new is empty; no reason to replace old.
- return nil
+ return false, nil
+ }
+ if !oldIsEmpty {
+ return false, fmt.Errorf("template: redefinition of template %q", name)
}
}
t.tmpl[name] = new
- return nil
+ return true, nil
}