summaryrefslogtreecommitdiff
path: root/src/pkg/exp/template
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/exp/template')
-rw-r--r--src/pkg/exp/template/Makefile15
-rw-r--r--src/pkg/exp/template/exec.go508
-rw-r--r--src/pkg/exp/template/exec_test.go342
-rw-r--r--src/pkg/exp/template/funcs.go294
-rw-r--r--src/pkg/exp/template/lex.go431
-rw-r--r--src/pkg/exp/template/lex_test.go153
-rw-r--r--src/pkg/exp/template/parse.go783
-rw-r--r--src/pkg/exp/template/parse_test.go207
-rw-r--r--src/pkg/exp/template/set.go115
-rw-r--r--src/pkg/exp/template/set_test.go101
10 files changed, 0 insertions, 2949 deletions
diff --git a/src/pkg/exp/template/Makefile b/src/pkg/exp/template/Makefile
deleted file mode 100644
index 8550b0d52..000000000
--- a/src/pkg/exp/template/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2011 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include ../../../Make.inc
-
-TARG=exp/template
-GOFILES=\
- exec.go\
- funcs.go\
- lex.go\
- parse.go\
- set.go\
-
-include ../../../Make.pkg
diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go
deleted file mode 100644
index fb0a9e621..000000000
--- a/src/pkg/exp/template/exec.go
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "io"
- "os"
- "reflect"
- "strings"
- "unicode"
- "utf8"
-)
-
-// state represents the state of an execution. It's not part of the
-// template so that multiple executions of the same template
-// can execute in parallel.
-type state struct {
- tmpl *Template
- wr io.Writer
- set *Set
- line int // line number for errors
-}
-
-// errorf formats the error and terminates processing.
-func (s *state) errorf(format string, args ...interface{}) {
- format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.name, s.line, format)
- panic(fmt.Errorf(format, args...))
-}
-
-// error terminates processing.
-func (s *state) error(err os.Error) {
- s.errorf("%s", err)
-}
-
-// Execute applies a parsed template to the specified data object,
-// writing the output to wr.
-func (t *Template) Execute(wr io.Writer, data interface{}) os.Error {
- return t.ExecuteInSet(wr, data, nil)
-}
-
-// ExecuteInSet applies a parsed template to the specified data object,
-// writing the output to wr. Nested template invocations will be resolved
-// from the specified set.
-func (t *Template) ExecuteInSet(wr io.Writer, data interface{}, set *Set) (err os.Error) {
- defer t.recover(&err)
- state := &state{
- tmpl: t,
- wr: wr,
- set: set,
- line: 1,
- }
- if t.root == nil {
- state.errorf("must be parsed before execution")
- }
- state.walk(reflect.ValueOf(data), t.root)
- return
-}
-
-// Walk functions step through the major pieces of the template structure,
-// generating output as they go.
-func (s *state) walk(data reflect.Value, n node) {
- switch n := n.(type) {
- case *actionNode:
- s.line = n.line
- s.printValue(n, s.evalPipeline(data, n.pipeline))
- case *listNode:
- for _, node := range n.nodes {
- s.walk(data, node)
- }
- case *ifNode:
- s.line = n.line
- s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList)
- case *rangeNode:
- s.line = n.line
- s.walkRange(data, n)
- case *textNode:
- if _, err := s.wr.Write(n.text); err != nil {
- s.error(err)
- }
- case *templateNode:
- s.line = n.line
- s.walkTemplate(data, n)
- case *withNode:
- s.line = n.line
- s.walkIfOrWith(nodeWith, data, n.pipeline, n.list, n.elseList)
- default:
- s.errorf("unknown node: %s", n)
- }
-}
-
-// walkIfOrWith walks an 'if' or 'with' node. The two control structures
-// 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, 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
- case reflect.Bool:
- truth = val.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- truth = val.Int() != 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- truth = val.Uint() != 0
- case reflect.Float32, reflect.Float64:
- truth = val.Float() != 0
- case reflect.Complex64, reflect.Complex128:
- truth = val.Complex() != 0
- case reflect.Chan, reflect.Func, reflect.Ptr:
- truth = !val.IsNil()
- default:
- return
- }
- return truth, true
-}
-
-func (s *state) walkRange(data reflect.Value, r *rangeNode) {
- val := s.evalPipeline(data, r.pipeline)
- switch val.Kind() {
- case reflect.Array, reflect.Slice:
- if val.Len() == 0 {
- break
- }
- for i := 0; i < val.Len(); i++ {
- s.walk(val.Index(i), r.list)
- }
- return
- case reflect.Map:
- if val.Len() == 0 {
- break
- }
- for _, key := range val.MapKeys() {
- s.walk(val.MapIndex(key), r.list)
- }
- return
- default:
- s.errorf("range can't iterate over value of type %T", val.Interface())
- }
- if r.elseList != nil {
- s.walk(data, r.elseList)
- }
-}
-
-func (s *state) walkTemplate(data reflect.Value, t *templateNode) {
- name := s.evalArg(data, reflect.TypeOf("string"), t.name).String()
- if s.set == nil {
- s.errorf("no set defined in which to invoke template named %q", name)
- }
- tmpl := s.set.tmpl[name]
- if tmpl == nil {
- s.errorf("template %q not in set", name)
- }
- data = s.evalPipeline(data, t.pipeline)
- newState := *s
- newState.tmpl = tmpl
- newState.walk(data, tmpl.root)
-}
-
-// Eval functions evaluate pipelines, commands, and their elements and extract
-// values from the data structure by examining fields, calling methods, and so on.
-// The printing of those values happens only through walk functions.
-
-func (s *state) evalPipeline(data reflect.Value, pipe []*commandNode) reflect.Value {
- value := reflect.Value{}
- for _, cmd := range pipe {
- value = s.evalCommand(data, cmd, value) // previous value is this one's final arg.
- // If the object has type interface{}, dig down one level to the thing inside.
- if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
- value = reflect.ValueOf(value.Interface()) // lovely!
- }
- }
- return value
-}
-
-func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value {
- firstWord := cmd.args[0]
- switch n := firstWord.(type) {
- case *fieldNode:
- return s.evalFieldNode(data, n, cmd.args, final)
- case *identifierNode:
- return s.evalFieldOrCall(data, n.ident, cmd.args, final)
- }
- if len(cmd.args) > 1 || final.IsValid() {
- s.errorf("can't give argument to non-function %s", cmd.args[0])
- }
- switch word := cmd.args[0].(type) {
- case *dotNode:
- return data
- case *boolNode:
- return reflect.ValueOf(word.true)
- case *numberNode:
- // These are ideal constants but we don't know the type
- // and we have no context. (If it was a method argument,
- // we'd know what we need.) The syntax guides us to some extent.
- switch {
- case word.isComplex:
- return reflect.ValueOf(word.complex128) // incontrovertible.
- case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0:
- return reflect.ValueOf(word.float64)
- case word.isInt:
- return reflect.ValueOf(word.int64)
- case word.isUint:
- return reflect.ValueOf(word.uint64)
- }
- case *stringNode:
- return reflect.ValueOf(word.text)
- }
- s.errorf("can't handle command %q", firstWord)
- panic("not reached")
-}
-
-func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value {
- // Up to the last entry, it must be a field.
- n := len(field.ident)
- for i := 0; i < n-1; i++ {
- data = s.evalField(data, field.ident[i])
- }
- // Now it can be a field or method and if a method, gets arguments.
- return s.evalFieldOrCall(data, field.ident[n-1], args, final)
-}
-
-// Is this an exported - upper case - name?
-func isExported(name string) bool {
- rune, _ := utf8.DecodeRuneInString(name)
- return unicode.IsUpper(rune)
-}
-
-func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value {
- var isNil bool
- if data, isNil = indirect(data); isNil {
- s.errorf("%s is nil pointer", fieldName)
- }
- switch data.Kind() {
- case reflect.Struct:
- // Is it a field?
- field := data.FieldByName(fieldName)
- // TODO: look higher up the tree if we can't find it here. Also unexported fields
- // might succeed higher up, as map keys.
- if field.IsValid() && isExported(fieldName) { // valid and exported
- return field
- }
- s.errorf("%s has no exported field %q", data.Type(), fieldName)
- default:
- s.errorf("can't evaluate field %s of type %s", fieldName, data.Type())
- }
- panic("not reached")
-}
-
-func (s *state) evalFieldOrCall(data reflect.Value, fieldName string, args []node, final reflect.Value) reflect.Value {
- // Is it a function?
- if function, ok := findFunction(fieldName, s.tmpl, s.set); ok {
- return s.evalCall(data, function, fieldName, false, args, final)
- }
- ptr := data
- for data.Kind() == reflect.Ptr && !data.IsNil() {
- ptr, data = data, reflect.Indirect(data)
- }
- // Is it a method? We use the pointer because it has value methods too.
- if method, ok := methodByName(ptr.Type(), fieldName); ok {
- return s.evalCall(ptr, method.Func, fieldName, true, args, final)
- }
- if len(args) > 1 || final.IsValid() {
- s.errorf("%s is not a method but has arguments", fieldName)
- }
- switch data.Kind() {
- case reflect.Struct:
- return s.evalField(data, fieldName)
- default:
- s.errorf("can't handle evaluation of field %s of type %s", fieldName, data.Type())
- }
- panic("not reached")
-}
-
-// TODO: delete when reflect's own MethodByName is released.
-func methodByName(typ reflect.Type, name string) (reflect.Method, bool) {
- for i := 0; i < typ.NumMethod(); i++ {
- if typ.Method(i).Name == name {
- return typ.Method(i), true
- }
- }
- return reflect.Method{}, false
-}
-
-var (
- osErrorType = reflect.TypeOf(new(os.Error)).Elem()
-)
-
-func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args []node, final reflect.Value) reflect.Value {
- typ := fun.Type()
- if !isMethod && len(args) > 0 { // Args will be nil if it's a niladic call in an argument list
- args = args[1:] // first arg is name of function; not used in call.
- }
- numIn := len(args)
- if final.IsValid() {
- numIn++
- }
- numFixed := len(args)
- if typ.IsVariadic() {
- numFixed = typ.NumIn() - 1 // last arg is the variadic one.
- if numIn < numFixed {
- s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
- }
- } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
- s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
- }
- if !goodFunc(typ) {
- s.errorf("can't handle multiple results from method/function %q", name)
- }
- // Build the arg list.
- argv := make([]reflect.Value, numIn)
- // First arg is the receiver.
- i := 0
- if isMethod {
- argv[0] = v
- i++
- }
- // Others must be evaluated. Fixed args first.
- for ; i < numFixed; i++ {
- argv[i] = s.evalArg(v, typ.In(i), args[i])
- }
- // And now the ... args.
- if typ.IsVariadic() {
- argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
- for ; i < len(args); i++ {
- argv[i] = s.evalArg(v, argType, args[i])
- }
- }
- // Add final value if necessary.
- if final.IsValid() {
- argv[len(args)] = final
- }
- result := fun.Call(argv)
- // If we have an os.Error that is not nil, stop execution and return that error to the caller.
- if len(result) == 2 && !result[1].IsNil() {
- s.error(result[1].Interface().(os.Error))
- }
- return result[0]
-}
-
-func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value {
- if field, ok := n.(*fieldNode); ok {
- value := s.evalFieldNode(data, field, []node{n}, reflect.Value{})
- if !value.Type().AssignableTo(typ) {
- s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
- }
- return value
- }
- switch typ.Kind() {
- case reflect.Bool:
- return s.evalBool(typ, n)
- case reflect.String:
- return s.evalString(typ, n)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return s.evalInteger(typ, n)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return s.evalUnsignedInteger(typ, n)
- case reflect.Float32, reflect.Float64:
- return s.evalFloat(typ, n)
- case reflect.Complex64, reflect.Complex128:
- return s.evalComplex(typ, n)
- case reflect.Interface:
- if typ.NumMethod() == 0 {
- return s.evalEmptyInterface(data, typ, n)
- }
- }
- s.errorf("can't handle %s for arg of type %s", n, typ)
- panic("not reached")
-}
-
-func (s *state) evalBool(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*boolNode); ok {
- value := reflect.New(typ).Elem()
- value.SetBool(n.true)
- return value
- }
- s.errorf("expected bool; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalString(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*stringNode); ok {
- value := reflect.New(typ).Elem()
- value.SetString(n.text)
- return value
- }
- s.errorf("expected string; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalInteger(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*numberNode); ok && n.isInt {
- value := reflect.New(typ).Elem()
- value.SetInt(n.int64)
- return value
- }
- s.errorf("expected integer; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalUnsignedInteger(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*numberNode); ok && n.isUint {
- value := reflect.New(typ).Elem()
- value.SetUint(n.uint64)
- return value
- }
- s.errorf("expected unsigned integer; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalFloat(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*numberNode); ok && n.isFloat {
- value := reflect.New(typ).Elem()
- value.SetFloat(n.float64)
- return value
- }
- s.errorf("expected float; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalComplex(typ reflect.Type, n node) reflect.Value {
- if n, ok := n.(*numberNode); ok && n.isComplex {
- value := reflect.New(typ).Elem()
- value.SetComplex(n.complex128)
- return value
- }
- s.errorf("expected complex; found %s", n)
- panic("not reached")
-}
-
-func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value {
- switch n := n.(type) {
- case *boolNode:
- return reflect.ValueOf(n.true)
- case *dotNode:
- return data
- case *fieldNode:
- return s.evalFieldNode(data, n, nil, reflect.Value{})
- case *identifierNode:
- return s.evalFieldOrCall(data, n.ident, nil, reflect.Value{})
- case *numberNode:
- if n.isComplex {
- return reflect.ValueOf(n.complex128)
- }
- if n.isInt {
- return reflect.ValueOf(n.int64)
- }
- if n.isUint {
- return reflect.ValueOf(n.uint64)
- }
- if n.isFloat {
- return reflect.ValueOf(n.float64)
- }
- case *stringNode:
- return reflect.ValueOf(n.text)
- }
- s.errorf("can't handle assignment of %s to empty interface argument", n)
- panic("not reached")
-}
-
-// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
-func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
- for v.Kind() == reflect.Ptr {
- if v.IsNil() {
- return v, true
- }
- v = v.Elem()
- }
- return v, false
-}
-
-// printValue writes the textual representation of the value to the output of
-// the template.
-func (s *state) printValue(n node, v reflect.Value) {
- if !v.IsValid() {
- fmt.Fprint(s.wr, "<no value>")
- return
- }
- switch v.Kind() {
- case reflect.Ptr:
- var isNil bool
- if v, isNil = indirect(v); isNil {
- fmt.Fprint(s.wr, "<nil>")
- return
- }
- case reflect.Chan, reflect.Func, reflect.Interface:
- s.errorf("can't print %s of type %s", n, v.Type())
- }
- fmt.Fprint(s.wr, v.Interface())
-}
diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go
deleted file mode 100644
index 86b958e84..000000000
--- a/src/pkg/exp/template/exec_test.go
+++ /dev/null
@@ -1,342 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "os"
- "sort"
- "strings"
- "testing"
-)
-
-// T has lots of interesting pieces to use to test execution.
-type T struct {
- // Basics
- I int
- U16 uint16
- X string
- FloatZero float64
- ComplexZero float64
- // Nested structs.
- U *U
- // Slices
- SI []int
- SIEmpty []int
- SB []bool
- // Maps
- MSI map[string]int
- MSIone map[string]int // one element, for deterministic output
- MSIEmpty map[string]int
- SMSI []map[string]int
- // Empty interfaces; used to see if we can dig inside one.
- Empty0 interface{} // nil
- Empty1 interface{}
- Empty2 interface{}
- Empty3 interface{}
- Empty4 interface{}
- // Pointers
- PI *int
- PSI *[]int
- NIL *int
-}
-
-var tVal = &T{
- I: 17,
- U16: 16,
- X: "x",
- U: &U{"v"},
- SI: []int{3, 4, 5},
- SB: []bool{true, false},
- MSI: map[string]int{"one": 1, "two": 2, "three": 3},
- MSIone: map[string]int{"one": 1},
- SMSI: []map[string]int{
- {"one": 1, "two": 2},
- {"eleven": 11, "twelve": 12},
- },
- Empty1: 3,
- Empty2: "empty2",
- Empty3: []int{7, 8},
- Empty4: &U{"v"},
- PI: newInt(23),
- PSI: newIntSlice(21, 22, 23),
-}
-
-// Helpers for creation.
-func newInt(n int) *int {
- p := new(int)
- *p = n
- return p
-}
-
-func newIntSlice(n ...int) *[]int {
- p := new([]int)
- *p = make([]int, len(n))
- copy(*p, n)
- return p
-}
-
-// Simple methods with and without arguments.
-func (t *T) Method0() string {
- return "resultOfMethod0"
-}
-
-func (t *T) Method1(a int) int {
- return a
-}
-
-func (t *T) Method2(a uint16, b string) string {
- return fmt.Sprintf("Method2: %d %s", a, b)
-}
-
-func (t *T) MAdd(a int, b []int) []int {
- v := make([]int, len(b))
- for i, x := range b {
- v[i] = x + a
- }
- return v
-}
-
-// MSort is used to sort map keys for stable output. (Nice trick!)
-func (t *T) MSort(m map[string]int) []string {
- keys := make([]string, len(m))
- i := 0
- for k := range m {
- keys[i] = k
- i++
- }
- sort.Strings(keys)
- return keys
-}
-
-// EPERM returns a value and an os.Error according to its argument.
-func (t *T) EPERM(error bool) (bool, os.Error) {
- if error {
- return true, os.EPERM
- }
- return false, nil
-}
-
-type U struct {
- V string
-}
-
-type execTest struct {
- name string
- input string
- output string
- data interface{}
- ok bool
-}
-
-var execTests = []execTest{
- // Trivial cases.
- {"empty", "", "", nil, true},
- {"text", "some text", "some text", nil, true},
-
- // Fields of structs.
- {".X", "-{{.X}}-", "-x-", tVal, true},
- {".U.V", "-{{.U.V}}-", "-v-", tVal, true},
-
- // Dots of all kinds to test basic evaluation.
- {"dot int", "<{{.}}>", "<13>", 13, true},
- {"dot uint", "<{{.}}>", "<14>", uint(14), true},
- {"dot float", "<{{.}}>", "<15.1>", 15.1, true},
- {"dot bool", "<{{.}}>", "<true>", true, true},
- {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true},
- {"dot string", "<{{.}}>", "<hello>", "hello", true},
- {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true},
- {"dot map", "<{{.}}>", "<map[two:22 one:11]>", map[string]int{"one": 11, "two": 22}, true},
- {"dot struct", "<{{.}}>", "<{7 seven}>", struct {
- a int
- b string
- }{7, "seven"}, true},
-
- // Pointers.
- {"*int", "{{.PI}}", "23", tVal, true},
- {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true},
- {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true},
- {"NIL", "{{.NIL}}", "<nil>", tVal, true},
-
- // Emtpy interfaces holding values.
- {"empty nil", "{{.Empty0}}", "<no value>", tVal, true},
- {"empty with int", "{{.Empty1}}", "3", tVal, true},
- {"empty with string", "{{.Empty2}}", "empty2", tVal, true},
- {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true},
- {"empty with struct", "{{.Empty4}}", "{v}", tVal, true},
-
- // Method calls.
- {".Method0", "-{{.Method0}}-", "-resultOfMethod0-", tVal, true},
- {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
- {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true},
- {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
- {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
-
- // Pipelines.
- {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 resultOfMethod0-", tVal, true},
-
- // If.
- {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
- {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
- {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
- {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
- {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
- {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
- {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
- {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "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 map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true},
-
- // 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},
- {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true},
- {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true},
- {"printf function", `{{printf "%#q" gopher}}`, "`gopher`", tVal, true},
- {"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>"}}`,
- "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
- {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
- "&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", nil, true},
-
- // JavaScript.
- {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, 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},
-
- // Indexing.
- {"slice[0]", "{{index .SI 0}}", "3", tVal, true},
- {"slice[1]", "{{index .SI 1}}", "4", tVal, true},
- {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false},
- {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
- {"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
- {"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
- {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, false},
- {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
- {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
-
- // With.
- {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
- {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
- {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true},
- {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true},
- {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true},
- {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true},
- {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
- {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true},
- {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
- {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true},
- {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
- {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true},
- {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true},
-
- // Range.
- {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true},
- {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
- {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
- {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
- {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
- {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
- {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true},
- {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
- {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", 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},
-
- // Error handling.
- {"error method, error", "{{.EPERM true}}", "", tVal, false},
- {"error method, no error", "{{.EPERM false}}", "false", tVal, true},
-}
-
-func gopher() string {
- return "gopher"
-}
-
-func testExecute(execTests []execTest, set *Set, t *testing.T) {
- b := new(bytes.Buffer)
- funcs := FuncMap{"gopher": gopher}
- for _, test := range execTests {
- tmpl := New(test.name).Funcs(funcs)
- err := tmpl.Parse(test.input)
- if err != nil {
- t.Errorf("%s: parse error: %s", test.name, err)
- continue
- }
- b.Reset()
- err = tmpl.ExecuteInSet(b, test.data, set)
- switch {
- case !test.ok && err == nil:
- t.Errorf("%s: expected error; got none", test.name)
- continue
- case test.ok && err != nil:
- t.Errorf("%s: unexpected execute error: %s", test.name, err)
- continue
- case !test.ok && err != nil:
- // expected error, got one
- if *debug {
- fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
- }
- }
- result := b.String()
- if result != test.output {
- t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result)
- }
- }
-}
-
-func TestExecute(t *testing.T) {
- testExecute(execTests, nil, t)
-}
-
-// Check that an error from a method flows back to the top.
-func TestExecuteError(t *testing.T) {
- b := new(bytes.Buffer)
- tmpl := New("error")
- err := tmpl.Parse("{{.EPERM true}}")
- if err != nil {
- t.Fatalf("parse error: %s", err)
- }
- err = tmpl.Execute(b, tVal)
- if err == nil {
- t.Errorf("expected error; got none")
- } else if !strings.Contains(err.String(), os.EPERM.String()) {
- t.Errorf("expected os.EPERM; got %s", err)
- }
-}
-
-func TestJSEscaping(t *testing.T) {
- testCases := []struct {
- in, exp string
- }{
- {`a`, `a`},
- {`'foo`, `\'foo`},
- {`Go "jump" \`, `Go \"jump\" \\`},
- {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
- {"unprintable \uFDFF", `unprintable \uFDFF`},
- }
- for _, tc := range testCases {
- s := JSEscapeString(tc.in)
- if s != tc.exp {
- t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp)
- }
- }
-}
diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go
deleted file mode 100644
index 66be40fd4..000000000
--- a/src/pkg/exp/template/funcs.go
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "io"
- "os"
- "reflect"
- "strings"
- "unicode"
- "utf8"
-)
-
-// FuncMap is the type of the map defining the mapping from names to functions.
-// Each function must have either a single return value, or two return values of
-// which the second has type os.Error.
-type FuncMap map[string]interface{}
-
-var funcs = map[string]reflect.Value{
- "and": reflect.ValueOf(and),
- "html": reflect.ValueOf(HTMLEscaper),
- "index": reflect.ValueOf(index),
- "js": reflect.ValueOf(JSEscaper),
- "not": reflect.ValueOf(not),
- "or": reflect.ValueOf(or),
- "printf": reflect.ValueOf(fmt.Sprintf),
-}
-
-// addFuncs adds to values the functions in funcs, converting them to reflect.Values.
-func addFuncs(values map[string]reflect.Value, funcMap FuncMap) {
- for name, fn := range funcMap {
- v := reflect.ValueOf(fn)
- if v.Kind() != reflect.Func {
- panic("value for " + name + " not a function")
- }
- if !goodFunc(v.Type()) {
- panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
- }
- values[name] = v
- }
-}
-
-// goodFunc checks that the function or method has the right result signature.
-func goodFunc(typ reflect.Type) bool {
- // We allow functions with 1 result or 2 results where the second is an os.Error.
- switch {
- case typ.NumOut() == 1:
- return true
- case typ.NumOut() == 2 && typ.Out(1) == osErrorType:
- return true
- }
- return false
-}
-
-// findFunction looks for a function in the template, set, and global map.
-func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
- if tmpl != nil {
- if fn := tmpl.funcs[name]; fn.IsValid() {
- return fn, true
- }
- }
- if set != nil {
- if fn := set.funcs[name]; fn.IsValid() {
- return fn, true
- }
- }
- if fn := funcs[name]; fn.IsValid() {
- return fn, true
- }
- return reflect.Value{}, false
-}
-
-// Indexing.
-
-// index returns the result of indexing its first argument by the following
-// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
-// indexed item must be a map, slice, or array.
-func index(item interface{}, indices ...interface{}) (interface{}, os.Error) {
- v := reflect.ValueOf(item)
- for _, i := range indices {
- index := reflect.ValueOf(i)
- var isNil bool
- if v, isNil = indirect(v); isNil {
- return nil, fmt.Errorf("index of nil pointer")
- }
- switch v.Kind() {
- case reflect.Array, reflect.Slice:
- var x int64
- switch index.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- x = index.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- x = int64(index.Uint())
- default:
- return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
- }
- if x < 0 || x >= int64(v.Len()) {
- return nil, fmt.Errorf("index out of range: %d", x)
- }
- v = v.Index(int(x))
- case reflect.Map:
- if !index.Type().AssignableTo(v.Type().Key()) {
- return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
- }
- v = v.MapIndex(index)
- if !v.IsValid() {
- return nil, fmt.Errorf("index %v not present in map", index.Interface())
- }
- default:
- return nil, fmt.Errorf("can't index item of type %s", index.Type())
- }
- }
- return v.Interface(), nil
-}
-
-// 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 (
- htmlQuot = []byte("&#34;") // shorter than "&quot;"
- htmlApos = []byte("&#39;") // shorter than "&apos;"
- htmlAmp = []byte("&amp;")
- htmlLt = []byte("&lt;")
- htmlGt = []byte("&gt;")
-)
-
-// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
-func HTMLEscape(w io.Writer, b []byte) {
- last := 0
- for i, c := range b {
- var html []byte
- switch c {
- case '"':
- html = htmlQuot
- case '\'':
- html = htmlApos
- case '&':
- html = htmlAmp
- case '<':
- html = htmlLt
- case '>':
- html = htmlGt
- default:
- continue
- }
- w.Write(b[last:i])
- w.Write(html)
- last = i + 1
- }
- w.Write(b[last:])
-}
-
-// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
-func HTMLEscapeString(s string) string {
- // Avoid allocation if we can.
- if strings.IndexAny(s, `'"&<>`) < 0 {
- return s
- }
- var b bytes.Buffer
- HTMLEscape(&b, []byte(s))
- return b.String()
-}
-
-// HTMLEscaper returns the escaped HTML equivalent of the textual
-// representation of its arguments.
-func HTMLEscaper(args ...interface{}) string {
- ok := false
- var s string
- if len(args) == 1 {
- s, ok = args[0].(string)
- }
- if !ok {
- s = fmt.Sprint(args...)
- }
- return HTMLEscapeString(s)
-}
-
-// JavaScript escaping.
-
-var (
- jsLowUni = []byte(`\u00`)
- hex = []byte("0123456789ABCDEF")
-
- jsBackslash = []byte(`\\`)
- jsApos = []byte(`\'`)
- jsQuot = []byte(`\"`)
-)
-
-
-// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
-func JSEscape(w io.Writer, b []byte) {
- last := 0
- for i := 0; i < len(b); i++ {
- c := b[i]
-
- if ' ' <= c && c < utf8.RuneSelf && c != '\\' && c != '"' && c != '\'' {
- // fast path: nothing to do
- continue
- }
- w.Write(b[last:i])
-
- if c < utf8.RuneSelf {
- // Quotes and slashes get quoted.
- // Control characters get written as \u00XX.
- switch c {
- case '\\':
- w.Write(jsBackslash)
- case '\'':
- w.Write(jsApos)
- case '"':
- w.Write(jsQuot)
- default:
- w.Write(jsLowUni)
- t, b := c>>4, c&0x0f
- w.Write(hex[t : t+1])
- w.Write(hex[b : b+1])
- }
- } else {
- // Unicode rune.
- rune, size := utf8.DecodeRune(b[i:])
- if unicode.IsPrint(rune) {
- w.Write(b[i : i+size])
- } else {
- // TODO(dsymonds): Do this without fmt?
- fmt.Fprintf(w, "\\u%04X", rune)
- }
- i += size - 1
- }
- last = i + 1
- }
- w.Write(b[last:])
-}
-
-// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
-func JSEscapeString(s string) string {
- // Avoid allocation if we can.
- if strings.IndexFunc(s, jsIsSpecial) < 0 {
- return s
- }
- var b bytes.Buffer
- JSEscape(&b, []byte(s))
- return b.String()
-}
-
-func jsIsSpecial(rune int) bool {
- switch rune {
- case '\\', '\'', '"':
- return true
- }
- return rune < ' ' || utf8.RuneSelf <= rune
-}
-
-// JSEscaper returns the escaped JavaScript equivalent of the textual
-// representation of its arguments.
-func JSEscaper(args ...interface{}) string {
- ok := false
- var s string
- if len(args) == 1 {
- s, ok = args[0].(string)
- }
- if !ok {
- s = fmt.Sprint(args...)
- }
- return JSEscapeString(s)
-}
diff --git a/src/pkg/exp/template/lex.go b/src/pkg/exp/template/lex.go
deleted file mode 100644
index d78152979..000000000
--- a/src/pkg/exp/template/lex.go
+++ /dev/null
@@ -1,431 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "strings"
- "unicode"
- "utf8"
-)
-
-// item represents a token or text string returned from the scanner.
-type item struct {
- typ itemType
- val string
-}
-
-func (i item) String() string {
- switch {
- case i.typ == itemEOF:
- return "EOF"
- case i.typ == itemError:
- return i.val
- case i.typ > itemKeyword:
- return fmt.Sprintf("<%s>", i.val)
- case len(i.val) > 10:
- return fmt.Sprintf("%.10q...", i.val)
- }
- return fmt.Sprintf("%q", i.val)
-}
-
-// itemType identifies the type of lex items.
-type itemType int
-
-const (
- itemError itemType = iota // error occurred; value is text of error
- itemBool // boolean constant
- itemComplex // complex constant (1+2i); imaginary is just a number
- itemEOF
- itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
- itemIdentifier // alphanumeric identifier
- itemLeftDelim // left action delimiter
- itemNumber // simple number, including imaginary
- itemPipe // pipe symbol
- itemRawString // raw quoted string (includes quotes)
- itemRightDelim // right action delimiter
- itemString // quoted string (includes quotes)
- itemText // plain text
- // Keywords appear after all the rest.
- itemKeyword // used only to delimit the keywords
- itemDot // the cursor, spelled '.'.
- itemDefine // define keyword
- itemElse // else keyword
- itemEnd // end keyword
- itemIf // if keyword
- itemRange // range keyword
- itemTemplate // template keyword
- itemWith // with keyword
-)
-
-// Make the types prettyprint.
-var itemName = map[itemType]string{
- itemError: "error",
- itemBool: "bool",
- itemComplex: "complex",
- itemEOF: "EOF",
- itemField: "field",
- itemIdentifier: "identifier",
- itemLeftDelim: "left delim",
- itemNumber: "number",
- itemPipe: "pipe",
- itemRawString: "raw string",
- itemRightDelim: "right delim",
- itemString: "string",
- // keywords
- itemDot: ".",
- itemDefine: "define",
- itemElse: "else",
- itemIf: "if",
- itemEnd: "end",
- itemRange: "range",
- itemTemplate: "template",
- itemWith: "with",
-}
-
-func (i itemType) String() string {
- s := itemName[i]
- if s == "" {
- return fmt.Sprintf("item%d", int(i))
- }
- return s
-}
-
-var key = map[string]itemType{
- ".": itemDot,
- "define": itemDefine,
- "else": itemElse,
- "end": itemEnd,
- "if": itemIf,
- "range": itemRange,
- "template": itemTemplate,
- "with": itemWith,
-}
-
-const eof = -1
-
-// stateFn represents the state of the scanner as a function that returns the next state.
-type stateFn func(*lexer) stateFn
-
-// lexer holds the state of the scanner.
-type lexer struct {
- name string // the name of the input; used only for error reports.
- input string // the string being scanned.
- state stateFn // the next lexing function to enter
- pos int // current position in the input.
- start int // start position of this item.
- width int // width of last rune read from input.
- items chan item // channel of scanned items.
-}
-
-// next returns the next rune in the input.
-func (l *lexer) next() (rune int) {
- if l.pos >= len(l.input) {
- l.width = 0
- return eof
- }
- rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
- l.pos += l.width
- return rune
-}
-
-// peek returns but does not consume the next rune in the input.
-func (l *lexer) peek() int {
- rune := l.next()
- l.backup()
- return rune
-}
-
-// backup steps back one rune. Can only be called once per call of next.
-func (l *lexer) backup() {
- l.pos -= l.width
-}
-
-// emit passes an item back to the client.
-func (l *lexer) emit(t itemType) {
- l.items <- item{t, l.input[l.start:l.pos]}
- l.start = l.pos
-}
-
-// ignore skips over the pending input before this point.
-func (l *lexer) ignore() {
- l.start = l.pos
-}
-
-// accept consumes the next rune if it's from the valid set.
-func (l *lexer) accept(valid string) bool {
- if strings.IndexRune(valid, l.next()) >= 0 {
- return true
- }
- l.backup()
- return false
-}
-
-// acceptRun consumes a run of runes from the valid set.
-func (l *lexer) acceptRun(valid string) {
- for strings.IndexRune(valid, l.next()) >= 0 {
- }
- l.backup()
-}
-
-// lineNumber reports which line we're on. Doing it this way
-// means we don't have to worry about peek double counting.
-func (l *lexer) lineNumber() int {
- return 1 + strings.Count(l.input[:l.pos], "\n")
-}
-
-// error returns an error token and terminates the scan by passing
-// back a nil pointer that will be the next state, terminating l.run.
-func (l *lexer) errorf(format string, args ...interface{}) stateFn {
- l.items <- item{itemError, fmt.Sprintf(format, args...)}
- return nil
-}
-
-// nextItem returns the next item from the input.
-func (l *lexer) nextItem() item {
- for {
- select {
- case item := <-l.items:
- return item
- default:
- l.state = l.state(l)
- }
- }
- panic("not reached")
-}
-
-// lex creates a new scanner for the input string.
-func lex(name, input string) *lexer {
- l := &lexer{
- name: name,
- input: input,
- state: lexText,
- items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
- }
- return l
-}
-
-// state functions
-
-const (
- leftDelim = "{{"
- rightDelim = "}}"
- leftComment = "{{/*"
- rightComment = "*/}}"
-)
-
-// lexText scans until an opening action delimiter, "{{".
-func lexText(l *lexer) stateFn {
- for {
- if strings.HasPrefix(l.input[l.pos:], leftDelim) {
- if l.pos > l.start {
- l.emit(itemText)
- }
- return lexLeftDelim
- }
- if l.next() == eof {
- break
- }
- }
- // Correctly reached EOF.
- if l.pos > l.start {
- l.emit(itemText)
- }
- l.emit(itemEOF)
- return nil
-}
-
-// lexLeftDelim scans the left delimiter, which is known to be present.
-func lexLeftDelim(l *lexer) stateFn {
- if strings.HasPrefix(l.input[l.pos:], leftComment) {
- return lexComment
- }
- l.pos += len(leftDelim)
- l.emit(itemLeftDelim)
- return lexInsideAction
-}
-
-// lexComment scans a comment. The left comment marker is known to be present.
-func lexComment(l *lexer) stateFn {
- i := strings.Index(l.input[l.pos:], rightComment)
- if i < 0 {
- return l.errorf("unclosed comment")
- }
- l.pos += i + len(rightComment)
- l.ignore()
- return lexText
-}
-
-// lexRightDelim scans the right delimiter, which is known to be present.
-func lexRightDelim(l *lexer) stateFn {
- l.pos += len(rightDelim)
- l.emit(itemRightDelim)
- return lexText
-}
-
-// lexInsideAction scans the elements inside action delimiters.
-func lexInsideAction(l *lexer) stateFn {
- // Either number, quoted string, or identifier.
- // Spaces separate and are ignored.
- // Pipe symbols separate and are emitted.
- for {
- if strings.HasPrefix(l.input[l.pos:], rightDelim) {
- return lexRightDelim
- }
- switch r := l.next(); {
- case r == eof || r == '\n':
- return l.errorf("unclosed action")
- case isSpace(r):
- l.ignore()
- case r == '|':
- l.emit(itemPipe)
- case r == '"':
- return lexQuote
- case r == '`':
- return lexRawQuote
- case r == '.':
- // special look-ahead for ".field" so we don't break l.backup().
- if l.pos < len(l.input) {
- r := l.input[l.pos]
- if r < '0' || '9' < r {
- return lexIdentifier // itemDot comes from the keyword table.
- }
- }
- fallthrough // '.' can start a number.
- case r == '+' || r == '-' || ('0' <= r && r <= '9'):
- l.backup()
- return lexNumber
- case isAlphaNumeric(r):
- l.backup()
- return lexIdentifier
- default:
- return l.errorf("unrecognized character in action: %#U", r)
- }
- }
- return nil
-}
-
-// lexIdentifier scans an alphanumeric or field.
-func lexIdentifier(l *lexer) stateFn {
-Loop:
- for {
- switch r := l.next(); {
- case isAlphaNumeric(r):
- // absorb.
- case r == '.' && l.input[l.start] == '.':
- // field chaining; absorb into one token.
- default:
- l.backup()
- word := l.input[l.start:l.pos]
- switch {
- case key[word] > itemKeyword:
- l.emit(key[word])
- case word[0] == '.':
- l.emit(itemField)
- case word == "true", word == "false":
- l.emit(itemBool)
- default:
- l.emit(itemIdentifier)
- }
- break Loop
- }
- }
- return lexInsideAction
-}
-
-// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
-// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
-// and "089" - but when it's wrong the input is invalid and the parser (via
-// strconv) will notice.
-func lexNumber(l *lexer) stateFn {
- if !l.scanNumber() {
- return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
- }
- if sign := l.peek(); sign == '+' || sign == '-' {
- // Complex: 1+2i. No spaces, must end in 'i'.
- if !l.scanNumber() || l.input[l.pos-1] != 'i' {
- return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
- }
- l.emit(itemComplex)
- } else {
- l.emit(itemNumber)
- }
- return lexInsideAction
-}
-
-func (l *lexer) scanNumber() bool {
- // Optional leading sign.
- l.accept("+-")
- // Is it hex?
- digits := "0123456789"
- if l.accept("0") && l.accept("xX") {
- digits = "0123456789abcdefABCDEF"
- }
- l.acceptRun(digits)
- if l.accept(".") {
- l.acceptRun(digits)
- }
- if l.accept("eE") {
- l.accept("+-")
- l.acceptRun("0123456789")
- }
- // Is it imaginary?
- l.accept("i")
- // Next thing mustn't be alphanumeric.
- if isAlphaNumeric(l.peek()) {
- l.next()
- return false
- }
- return true
-}
-
-// lexQuote scans a quoted string.
-func lexQuote(l *lexer) stateFn {
-Loop:
- for {
- switch l.next() {
- case '\\':
- if r := l.next(); r != eof && r != '\n' {
- break
- }
- fallthrough
- case eof, '\n':
- return l.errorf("unterminated quoted string")
- case '"':
- break Loop
- }
- }
- l.emit(itemString)
- return lexInsideAction
-}
-
-// lexRawQuote scans a raw quoted string.
-func lexRawQuote(l *lexer) stateFn {
-Loop:
- for {
- switch l.next() {
- case eof, '\n':
- return l.errorf("unterminated raw quoted string")
- case '`':
- break Loop
- }
- }
- l.emit(itemRawString)
- return lexInsideAction
-}
-
-// isSpace reports whether r is a space character.
-func isSpace(r int) bool {
- switch r {
- case ' ', '\t', '\n', '\r':
- return true
- }
- return false
-}
-
-// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
-func isAlphaNumeric(r int) bool {
- return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
-}
diff --git a/src/pkg/exp/template/lex_test.go b/src/pkg/exp/template/lex_test.go
deleted file mode 100644
index 256ec04d8..000000000
--- a/src/pkg/exp/template/lex_test.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "reflect"
- "testing"
-)
-
-type lexTest struct {
- name string
- input string
- items []item
-}
-
-var (
- tEOF = item{itemEOF, ""}
- tLeft = item{itemLeftDelim, "{{"}
- tRight = item{itemRightDelim, "}}"}
- tRange = item{itemRange, "range"}
- tPipe = item{itemPipe, "|"}
- tFor = item{itemIdentifier, "for"}
- tQuote = item{itemString, `"abc \n\t\" "`}
- raw = "`" + `abc\n\t\" ` + "`"
- tRawQuote = item{itemRawString, raw}
-)
-
-var lexTests = []lexTest{
- {"empty", "", []item{tEOF}},
- {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
- {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
- {"text with comment", "hello-{{/* this is a comment */}}-world", []item{
- {itemText, "hello-"},
- {itemText, "-world"},
- tEOF,
- }},
- {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
- {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
- {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
- {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
- {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
- tLeft,
- {itemNumber, "1"},
- {itemNumber, "02"},
- {itemNumber, "0x14"},
- {itemNumber, "-7.2i"},
- {itemNumber, "1e3"},
- {itemNumber, "+1.2e-4"},
- {itemNumber, "4.2i"},
- {itemComplex, "1+2i"},
- tRight,
- tEOF,
- }},
- {"bools", "{{true false}}", []item{
- tLeft,
- {itemBool, "true"},
- {itemBool, "false"},
- tRight,
- tEOF,
- }},
- {"dot", "{{.}}", []item{
- tLeft,
- {itemDot, "."},
- tRight,
- tEOF,
- }},
- {"dots", "{{.x . .2 .x.y}}", []item{
- tLeft,
- {itemField, ".x"},
- {itemDot, "."},
- {itemNumber, ".2"},
- {itemField, ".x.y"},
- tRight,
- tEOF,
- }},
- {"keywords", "{{range if else end with}}", []item{
- tLeft,
- {itemRange, "range"},
- {itemIf, "if"},
- {itemElse, "else"},
- {itemEnd, "end"},
- {itemWith, "with"},
- tRight,
- tEOF,
- }},
- {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
- {itemText, "intro "},
- tLeft,
- {itemIdentifier, "echo"},
- {itemIdentifier, "hi"},
- {itemNumber, "1.2"},
- tPipe,
- {itemIdentifier, "noargs"},
- tPipe,
- {itemIdentifier, "args"},
- {itemNumber, "1"},
- {itemString, `"hi"`},
- tRight,
- {itemText, " outro"},
- tEOF,
- }},
- // errors
- {"badchar", "#{{#}}", []item{
- {itemText, "#"},
- tLeft,
- {itemError, "unrecognized character in action: U+0023 '#'"},
- }},
- {"unclosed action", "{{\n}}", []item{
- tLeft,
- {itemError, "unclosed action"},
- }},
- {"EOF in action", "{{range", []item{
- tLeft,
- tRange,
- {itemError, "unclosed action"},
- }},
- {"unclosed quote", "{{\"\n\"}}", []item{
- tLeft,
- {itemError, "unterminated quoted string"},
- }},
- {"unclosed raw quote", "{{`xx\n`}}", []item{
- tLeft,
- {itemError, "unterminated raw quoted string"},
- }},
- {"bad number", "{{3k}}", []item{
- tLeft,
- {itemError, `bad number syntax: "3k"`},
- }},
-}
-
-// collect gathers the emitted items into a slice.
-func collect(t *lexTest) (items []item) {
- l := lex(t.name, t.input)
- for {
- item := l.nextItem()
- items = append(items, item)
- if item.typ == itemEOF || item.typ == itemError {
- break
- }
- }
- return
-}
-
-func TestLex(t *testing.T) {
- for _, test := range lexTests {
- items := collect(&test)
- if !reflect.DeepEqual(items, test.items) {
- t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
- }
- }
-}
diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go
deleted file mode 100644
index 8b2d60207..000000000
--- a/src/pkg/exp/template/parse.go
+++ /dev/null
@@ -1,783 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "bytes"
- "fmt"
- "os"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "unicode"
-)
-
-// Template is the representation of a parsed template.
-type Template struct {
- name string
- root *listNode
- funcs map[string]reflect.Value
- // Parsing only; cleared after parse.
- set *Set
- lex *lexer
- token item // token lookahead for parser
- havePeek bool
-}
-
-// next returns the next token.
-func (t *Template) next() item {
- if t.havePeek {
- t.havePeek = false
- } else {
- t.token = t.lex.nextItem()
- }
- return t.token
-}
-
-// backup backs the input stream up one token.
-func (t *Template) backup() {
- t.havePeek = true
-}
-
-// peek returns but does not consume the next token.
-func (t *Template) peek() item {
- if t.havePeek {
- return t.token
- }
- t.token = t.lex.nextItem()
- t.havePeek = true
- return t.token
-}
-
-// A node is an element in the parse tree. The interface is trivial.
-type node interface {
- typ() nodeType
- String() string
-}
-
-type nodeType int
-
-func (t nodeType) typ() nodeType {
- return t
-}
-
-const (
- nodeText nodeType = iota
- nodeAction
- nodeCommand
- nodeDot
- nodeElse
- nodeEnd
- nodeField
- nodeIdentifier
- nodeIf
- nodeList
- nodeNumber
- nodeRange
- nodeString
- nodeTemplate
- nodeWith
-)
-
-// Nodes.
-
-// listNode holds a sequence of nodes.
-type listNode struct {
- nodeType
- nodes []node
-}
-
-func newList() *listNode {
- return &listNode{nodeType: nodeList}
-}
-
-func (l *listNode) append(n node) {
- l.nodes = append(l.nodes, n)
-}
-
-func (l *listNode) String() string {
- b := new(bytes.Buffer)
- fmt.Fprint(b, "[")
- for _, n := range l.nodes {
- fmt.Fprint(b, n)
- }
- fmt.Fprint(b, "]")
- return b.String()
-}
-
-// textNode holds plain text.
-type textNode struct {
- nodeType
- text []byte
-}
-
-func newText(text string) *textNode {
- return &textNode{nodeType: nodeText, text: []byte(text)}
-}
-
-func (t *textNode) String() string {
- return fmt.Sprintf("(text: %q)", t.text)
-}
-
-// actionNode holds an action (something bounded by delimiters).
-type actionNode struct {
- nodeType
- line int
- pipeline []*commandNode
-}
-
-func newAction(line int, pipeline []*commandNode) *actionNode {
- return &actionNode{nodeType: nodeAction, line: line, pipeline: pipeline}
-}
-
-func (a *actionNode) append(command *commandNode) {
- a.pipeline = append(a.pipeline, command)
-}
-
-func (a *actionNode) String() string {
- return fmt.Sprintf("(action: %v)", a.pipeline)
-}
-
-// commandNode holds a command (a pipeline inside an evaluating action).
-type commandNode struct {
- nodeType
- args []node // identifier, string, or number
-}
-
-func newCommand() *commandNode {
- return &commandNode{nodeType: nodeCommand}
-}
-
-func (c *commandNode) append(arg node) {
- c.args = append(c.args, arg)
-}
-
-func (c *commandNode) String() string {
- return fmt.Sprintf("(command: %v)", c.args)
-}
-
-// identifierNode holds an identifier.
-type identifierNode struct {
- nodeType
- ident string
-}
-
-func newIdentifier(ident string) *identifierNode {
- return &identifierNode{nodeType: nodeIdentifier, ident: ident}
-}
-
-func (i *identifierNode) String() string {
- return fmt.Sprintf("I=%s", i.ident)
-}
-
-// dotNode holds the special identifier '.'. It is represented by a nil pointer.
-type dotNode bool
-
-func newDot() *dotNode {
- return nil
-}
-
-func (d *dotNode) typ() nodeType {
- return nodeDot
-}
-
-func (d *dotNode) String() string {
- return "{{<.>}}"
-}
-
-// fieldNode holds a field (identifier starting with '.').
-// The names may be chained ('.x.y').
-// The period is dropped from each ident.
-type fieldNode struct {
- nodeType
- ident []string
-}
-
-func newField(ident string) *fieldNode {
- return &fieldNode{nodeType: nodeField, ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
-}
-
-func (f *fieldNode) String() string {
- return fmt.Sprintf("F=%s", f.ident)
-}
-
-// boolNode holds a boolean constant.
-type boolNode struct {
- nodeType
- true bool
-}
-
-func newBool(true bool) *boolNode {
- return &boolNode{nodeType: nodeString, true: true}
-}
-
-func (b *boolNode) String() string {
- if b.true {
- return fmt.Sprintf("B=true")
- }
- return fmt.Sprintf("B=false")
-}
-
-// numberNode holds a number, signed or unsigned integer, floating, or complex.
-// The value is parsed and stored under all the types that can represent the value.
-// This simulates in a small amount of code the behavior of Go's ideal constants.
-type numberNode struct {
- nodeType
- isInt bool // number has an integral value
- isUint bool // number has an unsigned integral value
- isFloat bool // number has a floating-point value
- isComplex bool // number is complex
- int64 // the signed integer value
- uint64 // the unsigned integer value
- float64 // the floating-point value
- complex128 // the complex value
- text string
-}
-
-func newNumber(text string, isComplex bool) (*numberNode, os.Error) {
- n := &numberNode{nodeType: nodeNumber, text: text}
- if isComplex {
- // fmt.Sscan can parse the pair, so let it do the work.
- if _, err := fmt.Sscan(text, &n.complex128); err != nil {
- return nil, err
- }
- n.isComplex = true
- n.simplifyComplex()
- return n, nil
- }
- // Imaginary constants can only be complex unless they are zero.
- if len(text) > 0 && text[len(text)-1] == 'i' {
- f, err := strconv.Atof64(text[:len(text)-1])
- if err == nil {
- n.isComplex = true
- n.complex128 = complex(0, f)
- n.simplifyComplex()
- return n, nil
- }
- }
- // Do integer test first so we get 0x123 etc.
- u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below.
- if err == nil {
- n.isUint = true
- n.uint64 = u
- }
- i, err := strconv.Btoi64(text, 0)
- if err == nil {
- n.isInt = true
- n.int64 = i
- if i == 0 {
- n.isUint = true // in case of -0.
- n.uint64 = u
- }
- }
- // If an integer extraction succeeded, promote the float.
- if n.isInt {
- n.isFloat = true
- n.float64 = float64(n.int64)
- } else if n.isUint {
- n.isFloat = true
- n.float64 = float64(n.uint64)
- } else {
- f, err := strconv.Atof64(text)
- if err == nil {
- n.isFloat = true
- n.float64 = f
- // If a floating-point extraction succeeded, extract the int if needed.
- if !n.isInt && float64(int64(f)) == f {
- n.isInt = true
- n.int64 = int64(f)
- }
- if !n.isUint && float64(uint64(f)) == f {
- n.isUint = true
- n.uint64 = uint64(f)
- }
- }
- }
- if !n.isInt && !n.isUint && !n.isFloat {
- return nil, fmt.Errorf("illegal number syntax: %q", text)
- }
- return n, nil
-}
-
-// simplifyComplex pulls out any other types that are represented by the complex number.
-// These all require that the imaginary part be zero.
-func (n *numberNode) simplifyComplex() {
- n.isFloat = imag(n.complex128) == 0
- if n.isFloat {
- n.float64 = real(n.complex128)
- n.isInt = float64(int64(n.float64)) == n.float64
- if n.isInt {
- n.int64 = int64(n.float64)
- }
- n.isUint = float64(uint64(n.float64)) == n.float64
- if n.isUint {
- n.uint64 = uint64(n.float64)
- }
- }
-}
-
-func (n *numberNode) String() string {
- return fmt.Sprintf("N=%s", n.text)
-}
-
-// stringNode holds a quoted string.
-type stringNode struct {
- nodeType
- text string
-}
-
-func newString(text string) *stringNode {
- return &stringNode{nodeType: nodeString, text: text}
-}
-
-func (s *stringNode) String() string {
- return fmt.Sprintf("S=%#q", s.text)
-}
-
-// endNode represents an {{end}} action. It is represented by a nil pointer.
-type endNode bool
-
-func newEnd() *endNode {
- return nil
-}
-
-func (e *endNode) typ() nodeType {
- return nodeEnd
-}
-
-func (e *endNode) String() string {
- return "{{end}}"
-}
-
-// elseNode represents an {{else}} action.
-type elseNode struct {
- nodeType
- line int
-}
-
-func newElse(line int) *elseNode {
- return &elseNode{nodeType: nodeElse, line: line}
-}
-
-func (e *elseNode) typ() nodeType {
- return nodeElse
-}
-
-func (e *elseNode) String() string {
- return "{{else}}"
-}
-// ifNode represents an {{if}} action and its commands.
-// TODO: what should evaluation look like? is a pipeline enough?
-type ifNode struct {
- nodeType
- line int
- pipeline []*commandNode
- list *listNode
- elseList *listNode
-}
-
-func newIf(line int, pipeline []*commandNode, list, elseList *listNode) *ifNode {
- return &ifNode{nodeType: nodeIf, line: line, pipeline: pipeline, list: list, elseList: elseList}
-}
-
-func (i *ifNode) String() string {
- if i.elseList != nil {
- return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.pipeline, i.list, i.elseList)
- }
- return fmt.Sprintf("({{if %s}} %s)", i.pipeline, i.list)
-}
-
-// rangeNode represents a {{range}} action and its commands.
-type rangeNode struct {
- nodeType
- line int
- pipeline []*commandNode
- list *listNode
- elseList *listNode
-}
-
-func newRange(line int, pipeline []*commandNode, list, elseList *listNode) *rangeNode {
- return &rangeNode{nodeType: nodeRange, line: line, pipeline: pipeline, list: list, elseList: elseList}
-}
-
-func (r *rangeNode) String() string {
- if r.elseList != nil {
- return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.pipeline, r.list, r.elseList)
- }
- return fmt.Sprintf("({{range %s}} %s)", r.pipeline, r.list)
-}
-
-// templateNode represents a {{template}} action.
-type templateNode struct {
- nodeType
- line int
- name node
- pipeline []*commandNode
-}
-
-func newTemplate(line int, name node, pipeline []*commandNode) *templateNode {
- return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipeline: pipeline}
-}
-
-func (t *templateNode) String() string {
- return fmt.Sprintf("{{template %s %s}}", t.name, t.pipeline)
-}
-
-// withNode represents a {{with}} action and its commands.
-type withNode struct {
- nodeType
- line int
- pipeline []*commandNode
- list *listNode
- elseList *listNode
-}
-
-func newWith(line int, pipeline []*commandNode, list, elseList *listNode) *withNode {
- return &withNode{nodeType: nodeWith, line: line, pipeline: pipeline, list: list, elseList: elseList}
-}
-
-func (w *withNode) String() string {
- if w.elseList != nil {
- return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.pipeline, w.list, w.elseList)
- }
- return fmt.Sprintf("({{with %s}} %s)", w.pipeline, w.list)
-}
-
-
-// Parsing.
-
-// New allocates a new template with the given name.
-func New(name string) *Template {
- return &Template{
- name: name,
- funcs: make(map[string]reflect.Value),
- }
-}
-
-// Funcs adds to the template's function map the elements of the
-// argument map. It panics if a value in the map is not a function
-// with appropriate return type.
-// The return value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap FuncMap) *Template {
- addFuncs(t.funcs, funcMap)
- return t
-}
-
-// errorf formats the error and terminates processing.
-func (t *Template) errorf(format string, args ...interface{}) {
- t.root = nil
- format = fmt.Sprintf("template: %s:%d: %s", t.name, t.lex.lineNumber(), format)
- panic(fmt.Errorf(format, args...))
-}
-
-// error terminates processing.
-func (t *Template) error(err os.Error) {
- t.errorf("%s", err)
-}
-
-// expect consumes the next token and guarantees it has the required type.
-func (t *Template) expect(expected itemType, context string) item {
- token := t.next()
- if token.typ != expected {
- t.errorf("expected %s in %s; got %s", expected, context, token)
- }
- return token
-}
-
-// unexpected complains about the token and terminates processing.
-func (t *Template) unexpected(token item, context string) {
- t.errorf("unexpected %s in %s", token, context)
-}
-
-// recover is the handler that turns panics into returns from the top
-// level of Parse or Execute.
-func (t *Template) recover(errp *os.Error) {
- e := recover()
- if e != nil {
- if _, ok := e.(runtime.Error); ok {
- panic(e)
- }
- t.stopParse()
- *errp = e.(os.Error)
- }
- return
-}
-
-// startParse starts the template parsing from the lexer.
-func (t *Template) startParse(set *Set, lex *lexer) {
- t.root = nil
- t.set = set
- t.lex = lex
-}
-
-// stopParse terminates parsing.
-func (t *Template) stopParse() {
- t.set, t.lex = nil, nil
-}
-
-// atEOF returns true if, possibly after spaces, we're at EOF.
-func (t *Template) atEOF() bool {
- for {
- token := t.peek()
- switch token.typ {
- case itemEOF:
- return true
- case itemText:
- for _, r := range token.val {
- if !unicode.IsSpace(r) {
- return false
- }
- }
- t.next() // skip spaces.
- continue
- }
- break
- }
- return false
-}
-
-// Parse parses the template definition string to construct an internal representation
-// of the template for execution.
-func (t *Template) Parse(s string) (err os.Error) {
- t.startParse(nil, lex(t.name, s))
- defer t.recover(&err)
- t.parse(true)
- t.stopParse()
- return
-}
-
-// ParseInSet parses the template definition string to construct an internal representation
-// of the template for execution. Function bindings are checked against those in the set.
-func (t *Template) ParseInSet(s string, set *Set) (err os.Error) {
- t.startParse(set, lex(t.name, s))
- defer t.recover(&err)
- t.parse(true)
- t.stopParse()
- return
-}
-
-// parse is the helper for Parse. It triggers an error if we expect EOF but don't reach it.
-func (t *Template) parse(toEOF bool) (next node) {
- t.root, next = t.itemList(true)
- if toEOF && next != nil {
- t.errorf("unexpected %s", next)
- }
- return next
-}
-
-// itemList:
-// textOrAction*
-// Terminates at EOF and at {{end}} or {{else}}, which is returned separately.
-// The toEOF flag tells whether we expect to reach EOF.
-func (t *Template) itemList(toEOF bool) (list *listNode, next node) {
- list = newList()
- for t.peek().typ != itemEOF {
- n := t.textOrAction()
- switch n.typ() {
- case nodeEnd, nodeElse:
- return list, n
- }
- list.append(n)
- }
- if !toEOF {
- t.unexpected(t.next(), "input")
- }
- return list, nil
-}
-
-// textOrAction:
-// text | action
-func (t *Template) textOrAction() node {
- switch token := t.next(); token.typ {
- case itemText:
- return newText(token.val)
- case itemLeftDelim:
- return t.action()
- default:
- t.unexpected(token, "input")
- }
- return nil
-}
-
-// Action:
-// control
-// command ("|" command)*
-// Left delim is past. Now get actions.
-// First word could be a keyword such as range.
-func (t *Template) action() (n node) {
- switch token := t.next(); token.typ {
- case itemElse:
- return t.elseControl()
- case itemEnd:
- return t.endControl()
- case itemIf:
- return t.ifControl()
- case itemRange:
- return t.rangeControl()
- case itemTemplate:
- return t.templateControl()
- case itemWith:
- return t.withControl()
- }
- t.backup()
- return newAction(t.lex.lineNumber(), t.pipeline("command"))
-}
-
-// Pipeline:
-// field or command
-// pipeline "|" pipeline
-func (t *Template) pipeline(context string) (pipe []*commandNode) {
- for {
- switch token := t.next(); token.typ {
- case itemRightDelim:
- if len(pipe) == 0 {
- t.errorf("missing value for %s", context)
- }
- return
- case itemBool, itemComplex, itemDot, itemField, itemIdentifier, itemNumber, itemRawString, itemString:
- t.backup()
- pipe = append(pipe, t.command())
- default:
- t.unexpected(token, context)
- }
- }
- return
-}
-
-func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) {
- lineNum = t.lex.lineNumber()
- pipe = t.pipeline(context)
- var next node
- list, next = t.itemList(false)
- switch next.typ() {
- case nodeEnd: //done
- case nodeElse:
- elseList, next = t.itemList(false)
- if next.typ() != nodeEnd {
- t.errorf("expected end; found %s", next)
- }
- elseList = elseList
- }
- return lineNum, pipe, list, elseList
-}
-
-// If:
-// {{if pipeline}} itemList {{end}}
-// {{if pipeline}} itemList {{else}} itemList {{end}}
-// If keyword is past.
-func (t *Template) ifControl() node {
- return newIf(t.parseControl("if"))
-}
-
-// Range:
-// {{range pipeline}} itemList {{end}}
-// {{range pipeline}} itemList {{else}} itemList {{end}}
-// Range keyword is past.
-func (t *Template) rangeControl() node {
- return newRange(t.parseControl("range"))
-}
-
-// With:
-// {{with pipeline}} itemList {{end}}
-// {{with pipeline}} itemList {{else}} itemList {{end}}
-// If keyword is past.
-func (t *Template) withControl() node {
- return newWith(t.parseControl("with"))
-}
-
-
-// End:
-// {{end}}
-// End keyword is past.
-func (t *Template) endControl() node {
- t.expect(itemRightDelim, "end")
- return newEnd()
-}
-
-// Else:
-// {{else}}
-// Else keyword is past.
-func (t *Template) elseControl() node {
- t.expect(itemRightDelim, "else")
- return newElse(t.lex.lineNumber())
-}
-
-// Template:
-// {{template stringValue pipeline}}
-// Template keyword is past. The name must be something that can evaluate
-// to a string.
-func (t *Template) templateControl() node {
- var name node
- switch token := t.next(); token.typ {
- case itemIdentifier:
- if _, ok := findFunction(token.val, t, t.set); !ok {
- t.errorf("function %q not defined", token.val)
- }
- name = newIdentifier(token.val)
- case itemDot:
- name = newDot()
- case itemField:
- name = newField(token.val)
- case itemString, itemRawString:
- s, err := strconv.Unquote(token.val)
- if err != nil {
- t.error(err)
- }
- name = newString(s)
- default:
- t.unexpected(token, "template invocation")
- }
- pipeline := t.pipeline("template")
- return newTemplate(t.lex.lineNumber(), name, pipeline)
-}
-
-// command:
-// space-separated arguments up to a pipeline character or right delimiter.
-// we consume the pipe character but leave the right delim to terminate the action.
-func (t *Template) command() *commandNode {
- cmd := newCommand()
-Loop:
- for {
- switch token := t.next(); token.typ {
- case itemRightDelim:
- t.backup()
- break Loop
- case itemPipe:
- break Loop
- case itemError:
- t.errorf("%s", token.val)
- case itemIdentifier:
- if _, ok := findFunction(token.val, t, t.set); !ok {
- t.errorf("function %q not defined", token.val)
- }
- cmd.append(newIdentifier(token.val))
- case itemDot:
- cmd.append(newDot())
- case itemField:
- cmd.append(newField(token.val))
- case itemBool:
- cmd.append(newBool(token.val == "true"))
- case itemComplex, itemNumber:
- number, err := newNumber(token.val, token.typ == itemComplex)
- if err != nil {
- t.error(err)
- }
- cmd.append(number)
- case itemString, itemRawString:
- s, err := strconv.Unquote(token.val)
- if err != nil {
- t.error(err)
- }
- cmd.append(newString(s))
- default:
- t.unexpected(token, "command")
- }
- }
- if len(cmd.args) == 0 {
- t.errorf("empty command")
- }
- return cmd
-}
diff --git a/src/pkg/exp/template/parse_test.go b/src/pkg/exp/template/parse_test.go
deleted file mode 100644
index 71580f8b6..000000000
--- a/src/pkg/exp/template/parse_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "flag"
- "fmt"
- "testing"
-)
-
-var debug = flag.Bool("debug", false, "show the errors produced by the tests")
-
-type numberTest struct {
- text string
- isInt bool
- isUint bool
- isFloat bool
- isComplex bool
- int64
- uint64
- float64
- complex128
-}
-
-var numberTests = []numberTest{
- // basics
- {"0", true, true, true, false, 0, 0, 0, 0},
- {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
- {"73", true, true, true, false, 73, 73, 73, 0},
- {"-73", true, false, true, false, -73, 0, -73, 0},
- {"+73", true, false, true, false, 73, 0, 73, 0},
- {"100", true, true, true, false, 100, 100, 100, 0},
- {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
- {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
- {"-1.2", false, false, true, false, 0, 0, -1.2, 0},
- {"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
- {"-1e19", false, false, true, false, 0, 0, -1e19, 0},
- {"4i", false, false, false, true, 0, 0, 0, 4i},
- {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
- // complex with 0 imaginary are float (and maybe integer)
- {"0i", true, true, true, true, 0, 0, 0, 0},
- {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
- {"-12+0i", true, false, true, true, -12, 0, -12, -12},
- {"13+0i", true, true, true, true, 13, 13, 13, 13},
- // funny bases
- {"0123", true, true, true, false, 0123, 0123, 0123, 0},
- {"-0x0", true, true, true, false, 0, 0, 0, 0},
- {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
- // some broken syntax
- {text: "+-2"},
- {text: "0x123."},
- {text: "1e."},
- {text: "0xi."},
- {text: "1+2."},
-}
-
-func TestNumberParse(t *testing.T) {
- for _, test := range numberTests {
- // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
- // because imaginary comes out as a number.
- var c complex128
- _, err := fmt.Sscan(test.text, &c)
- n, err := newNumber(test.text, err == nil)
- ok := test.isInt || test.isUint || test.isFloat || test.isComplex
- if ok && err != nil {
- t.Errorf("unexpected error for %q", test.text)
- continue
- }
- if !ok && err == nil {
- t.Errorf("expected error for %q", test.text)
- continue
- }
- if !ok {
- continue
- }
- if n.isComplex != test.isComplex {
- t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
- }
- if test.isInt {
- if !n.isInt {
- t.Errorf("expected integer for %q", test.text)
- }
- if n.int64 != test.int64 {
- t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64)
- }
- } else if n.isInt {
- t.Errorf("did not expect integer for %q", test.text)
- }
- if test.isUint {
- if !n.isUint {
- t.Errorf("expected unsigned integer for %q", test.text)
- }
- if n.uint64 != test.uint64 {
- t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64)
- }
- } else if n.isUint {
- t.Errorf("did not expect unsigned integer for %q", test.text)
- }
- if test.isFloat {
- if !n.isFloat {
- t.Errorf("expected float for %q", test.text)
- }
- if n.float64 != test.float64 {
- t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64)
- }
- } else if n.isFloat {
- t.Errorf("did not expect float for %q", test.text)
- }
- if test.isComplex {
- if !n.isComplex {
- t.Errorf("expected complex for %q", test.text)
- }
- if n.complex128 != test.complex128 {
- t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128)
- }
- } else if n.isComplex {
- t.Errorf("did not expect complex for %q", test.text)
- }
- }
-}
-
-type parseTest struct {
- name string
- input string
- ok bool
- result string
-}
-
-const (
- noError = true
- hasError = false
-)
-
-var parseTests = []parseTest{
- {"empty", "", noError,
- `[]`},
- {"spaces", " \t\n", noError,
- `[(text: " \t\n")]`},
- {"text", "some text", noError,
- `[(text: "some text")]`},
- {"emptyAction", "{{}}", hasError,
- `[(action: [])]`},
- {"field", "{{.X}}", noError,
- `[(action: [(command: [F=[X]])])]`},
- {"simple command", "{{printf}}", noError,
- `[(action: [(command: [I=printf])])]`},
- {"multi-word command", "{{printf `%d` 23}}", noError,
- "[(action: [(command: [I=printf S=`%d` N=23])])]"},
- {"pipeline", "{{.X|.Y}}", noError,
- `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`},
- {"simple if", "{{if .X}}hello{{end}}", noError,
- `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`},
- {"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
- `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
- {"simple range", "{{range .X}}hello{{end}}", noError,
- `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`},
- {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
- `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`},
- {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
- `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`},
- {"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
- `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
- {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
- `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`},
- {"range []int", "{{range .SI}}{{.}}{{end}}", noError,
- `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`},
- {"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError,
- `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`},
- {"template", "{{template `x` .Y}}", noError,
- "[{{template S=`x` [(command: [F=[Y]])]}}]"},
- {"with", "{{with .X}}hello{{end}}", noError,
- `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`},
- {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
- `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`},
- // Errors.
- {"unclosed action", "hello{{range", hasError, ""},
- {"missing end", "hello{{range .x}}", hasError, ""},
- {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
- {"undefined function", "hello{{undefined}}", hasError, ""},
-}
-
-func TestParse(t *testing.T) {
- for _, test := range parseTests {
- tmpl := New(test.name)
- err := tmpl.Parse(test.input)
- switch {
- case err == nil && !test.ok:
- t.Errorf("%q: expected error; got none", test.name)
- continue
- case err != nil && test.ok:
- t.Errorf("%q: unexpected error: %v", test.name, err)
- continue
- case err != nil && !test.ok:
- // expected error, got one
- if *debug {
- fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
- }
- continue
- }
- result := tmpl.root.String()
- if result != test.result {
- t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
- }
- }
-}
diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go
deleted file mode 100644
index 492e270e1..000000000
--- a/src/pkg/exp/template/set.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "io"
- "os"
- "reflect"
- "runtime"
- "strconv"
-)
-
-// Set holds a set of related templates that can refer to one another by name.
-// A template may be a member of multiple sets.
-type Set struct {
- tmpl map[string]*Template
- funcs map[string]reflect.Value
-}
-
-// NewSet allocates a new, empty template set.
-func NewSet() *Set {
- return &Set{
- tmpl: make(map[string]*Template),
- funcs: make(map[string]reflect.Value),
- }
-}
-
-// Funcs adds to the set's function map the elements of the
-// argument map. It panics if a value in the map is not a function
-// with appropriate return type.
-// The return value is the set, so calls can be chained.
-func (s *Set) Funcs(funcMap FuncMap) *Set {
- addFuncs(s.funcs, funcMap)
- 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
-}
-
-// Template returns the template with the given name in the set,
-// or nil if there is no such template.
-func (s *Set) Template(name string) *Template {
- return s.tmpl[name]
-}
-
-// Execute looks for the named template in the set and then applies that
-// template to the specified data object, writing the output to wr. Nested
-// template invocations will be resolved from the set.
-func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error {
- tmpl := s.tmpl[name]
- if tmpl == nil {
- return fmt.Errorf("template: no template %q in set", name)
- }
- return tmpl.ExecuteInSet(wr, data, s)
-}
-
-// recover is the handler that turns panics into returns from the top
-// level of Parse.
-func (s *Set) recover(errp *os.Error) {
- e := recover()
- if e != nil {
- if _, ok := e.(runtime.Error); ok {
- panic(e)
- }
- s.tmpl = nil
- *errp = e.(os.Error)
- }
- return
-}
-
-// Parse parses the file into a set of named templates.
-func (s *Set) Parse(text string) (err os.Error) {
- defer s.recover(&err)
- lex := lex("set", text)
- const context = "define clause"
- for {
- t := New("set") // name will be updated once we know it.
- t.startParse(s, lex)
- // Expect EOF or "{{ define name }}".
- if t.atEOF() {
- return
- }
- t.expect(itemLeftDelim, context)
- t.expect(itemDefine, context)
- name := t.expect(itemString, context)
- t.name, err = strconv.Unquote(name.val)
- if err != nil {
- t.error(err)
- }
- t.expect(itemRightDelim, context)
- end := t.parse(false)
- if end == nil {
- t.errorf("unexpected EOF in %s", context)
- }
- if end.typ() != nodeEnd {
- t.errorf("unexpected %s in %s", end, context)
- }
- t.stopParse()
- s.tmpl[t.name] = t
- }
- return nil
-}
diff --git a/src/pkg/exp/template/set_test.go b/src/pkg/exp/template/set_test.go
deleted file mode 100644
index c0115ec0a..000000000
--- a/src/pkg/exp/template/set_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "fmt"
- "testing"
-)
-
-type setParseTest struct {
- name string
- input string
- ok bool
- names []string
- results []string
-}
-
-var setParseTests = []setParseTest{
- {"empty", "", noError,
- nil,
- nil},
- {"one", `{{define "foo"}} FOO {{end}}`, noError,
- []string{"foo"},
- []string{`[(text: " FOO ")]`}},
- {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
- []string{"foo", "bar"},
- []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}},
- // errors
- {"missing end", `{{define "foo"}} FOO `, hasError,
- nil,
- nil},
- {"malformed name", `{{define "foo}} FOO `, hasError,
- nil,
- nil},
-}
-
-func TestSetParse(t *testing.T) {
- for _, test := range setParseTests {
- set := NewSet()
- err := set.Parse(test.input)
- switch {
- case err == nil && !test.ok:
- t.Errorf("%q: expected error; got none", test.name)
- continue
- case err != nil && test.ok:
- t.Errorf("%q: unexpected error: %v", test.name, err)
- continue
- case err != nil && !test.ok:
- // expected error, got one
- if *debug {
- fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
- }
- continue
- }
- if len(set.tmpl) != len(test.names) {
- t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl))
- continue
- }
- for i, name := range test.names {
- tmpl, ok := set.tmpl[name]
- if !ok {
- t.Errorf("%s: can't find template %q", test.name, name)
- continue
- }
- result := tmpl.root.String()
- if result != test.results[i] {
- t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
- }
- }
- }
-}
-
-
-var setExecTests = []execTest{
- {"empty", "", "", nil, true},
- {"text", "some text", "some text", nil, true},
- {"invoke text", `{{template "text" .SI}}`, "TEXT", tVal, true},
- {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
- {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
- {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
- {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
-}
-
-const setText = `
- {{define "text"}}TEXT{{end}}
- {{define "dotV"}}{{.V}}{{end}}
- {{define "dot"}}{{.}}{{end}}
- {{define "nested"}}{{template "dot" .}}{{end}}
-`
-
-func TestSetExecute(t *testing.T) {
- // Declare a set with a couple of templates first.
- set := NewSet()
- err := set.Parse(setText)
- if err != nil {
- t.Fatalf("error parsing set: %s", err)
- }
- testExecute(setExecTests, set, t)
-}