diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-03-06 17:57:23 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-03-06 17:57:23 +0000 |
commit | 593f74bbab63d34c7060918088bcbad686c31c66 (patch) | |
tree | 4ce83ca433796a728e9fdd00af105bce158532b5 /libgo/go/text | |
parent | 46402cbe0ba3ea92be9642cf18eedaefe57a414c (diff) | |
download | gcc-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.go | 23 | ||||
-rw-r--r-- | libgo/go/text/template/exec.go | 5 | ||||
-rw-r--r-- | libgo/go/text/template/exec_test.go | 29 | ||||
-rw-r--r-- | libgo/go/text/template/funcs.go | 48 | ||||
-rw-r--r-- | libgo/go/text/template/multi_test.go | 6 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse.go | 2 | ||||
-rw-r--r-- | libgo/go/text/template/parse/parse_test.go | 3 | ||||
-rw-r--r-- | libgo/go/text/template/template.go | 20 |
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 } |