Commit 9ce770af authored by David Symonds's avatar David Symonds

old/regexp, old/template: delete.

Both of these have replacements.

R=golang-dev, r, rsc, r, adg
CC=golang-dev
https://golang.org/cl/5979046
parent 0bad08cb
......@@ -618,8 +618,6 @@ The packages in their new locations are:
<ul>
<li><code>old/netchan</code></li>
<li><code>old/regexp</code></li>
<li><code>old/template</code></li>
</ul>
<p>
......@@ -639,6 +637,8 @@ Go 1 deletes several packages outright:
<li><code>container/vector</code></li>
<li><code>exp/datafmt</code></li>
<li><code>go/typechecker</code></li>
<li><code>old/regexp</code></li>
<li><code>old/template</code></li>
<li><code>try</code></li>
</ul>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright 2009 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 implements data-driven templates for generating textual
output such as HTML.
Templates are executed by applying them to a data structure.
Annotations in the template refer to elements of the data
structure (typically a field of a struct or a key in a map)
to control execution and derive values to be displayed.
The template walks the structure as it executes and the
"cursor" @ represents the value at the current location
in the structure.
Data items may be values or pointers; the interface hides the
indirection.
In the following, 'Field' is one of several things, according to the data.
- The name of a field of a struct (result = data.Field),
- The value stored in a map under that key (result = data["Field"]), or
- The result of invoking a niladic single-valued method with that name
(result = data.Field())
If Field is a struct field or method name, it must be an exported
(capitalized) name.
Major constructs ({} are the default delimiters for template actions;
[] are the notation in this comment for optional elements):
{# comment }
A one-line comment.
{.section field} XXX [ {.or} YYY ] {.end}
Set @ to the value of the field. It may be an explicit @
to stay at the same point in the data. If the field is nil
or empty, execute YYY; otherwise execute XXX.
{.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end}
Like .section, but field must be an array or slice. XXX
is executed for each element. If the array is nil or empty,
YYY is executed instead. If the {.alternates with} marker
is present, ZZZ is executed between iterations of XXX.
{field}
{field1 field2 ...}
{field|formatter}
{field1 field2...|formatter}
{field|formatter1|formatter2}
Insert the value of the fields into the output. Each field is
first looked for in the cursor, as in .section and .repeated.
If it is not found, the search continues in outer sections
until the top level is reached.
If the field value is a pointer, leading asterisks indicate
that the value to be inserted should be evaluated through the
pointer. For example, if x.p is of type *int, {x.p} will
insert the value of the pointer but {*x.p} will insert the
value of the underlying integer. If the value is nil or not a
pointer, asterisks have no effect.
If a formatter is specified, it must be named in the formatter
map passed to the template set up routines or in the default
set ("html","str","") and is used to process the data for
output. The formatter function has signature
func(wr io.Writer, formatter string, data ...interface{})
where wr is the destination for output, data holds the field
values at the instantiation, and formatter is its name at
the invocation site. The default formatter just concatenates
the string representations of the fields.
Multiple formatters separated by the pipeline character | are
executed sequentially, with each formatter receiving the bytes
emitted by the one to its left.
As well as field names, one may use literals with Go syntax.
Integer, floating-point, and string literals are supported.
Raw strings may not span newlines.
The delimiter strings get their default value, "{" and "}", from
JSON-template. They may be set to any non-empty, space-free
string using the SetDelims method. Their value can be printed
in the output using {.meta-left} and {.meta-right}.
*/
package template
// Copyright 2009 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.
// Code to execute a parsed template.
package template
import (
"bytes"
"io"
"reflect"
"strings"
)
// Internal state for executing a Template. As we evaluate the struct,
// the data item descends into the fields associated with sections, etc.
// Parent is used to walk upwards to find variables higher in the tree.
type state struct {
parent *state // parent in hierarchy
data reflect.Value // the driver data for this section etc.
wr io.Writer // where to send output
buf [2]bytes.Buffer // alternating buffers used when chaining formatters
}
func (parent *state) clone(data reflect.Value) *state {
return &state{parent: parent, data: data, wr: parent.wr}
}
// Evaluate interfaces and pointers looking for a value that can look up the name, via a
// struct field, method, or map key, and return the result of the lookup.
func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value {
for v.IsValid() {
typ := v.Type()
if n := v.Type().NumMethod(); n > 0 {
for i := 0; i < n; i++ {
m := typ.Method(i)
mtyp := m.Type
if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 {
if !isExported(name) {
t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
}
return v.Method(i).Call(nil)[0]
}
}
}
switch av := v; av.Kind() {
case reflect.Ptr:
v = av.Elem()
case reflect.Interface:
v = av.Elem()
case reflect.Struct:
if !isExported(name) {
t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type())
}
return av.FieldByName(name)
case reflect.Map:
if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() {
return v
}
return reflect.Zero(typ.Elem())
default:
return reflect.Value{}
}
}
return v
}
// indirectPtr returns the item numLevels levels of indirection below the value.
// It is forgiving: if the value is not a pointer, it returns it rather than giving
// an error. If the pointer is nil, it is returned as is.
func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
for i := numLevels; v.IsValid() && i > 0; i++ {
if p := v; p.Kind() == reflect.Ptr {
if p.IsNil() {
return v
}
v = p.Elem()
} else {
break
}
}
return v
}
// Walk v through pointers and interfaces, extracting the elements within.
func indirect(v reflect.Value) reflect.Value {
loop:
for v.IsValid() {
switch av := v; av.Kind() {
case reflect.Ptr:
v = av.Elem()
case reflect.Interface:
v = av.Elem()
default:
break loop
}
}
return v
}
// If the data for this template is a struct, find the named variable.
// Names of the form a.b.c are walked down the data tree.
// The special name "@" (the "cursor") denotes the current data.
// The value coming in (st.data) might need indirecting to reach
// a struct while the return value is not indirected - that is,
// it represents the actual named field. Leading stars indicate
// levels of indirection to be applied to the value.
func (t *Template) findVar(st *state, s string) reflect.Value {
data := st.data
flattenedName := strings.TrimLeft(s, "*")
numStars := len(s) - len(flattenedName)
s = flattenedName
if s == "@" {
return indirectPtr(data, numStars)
}
for _, elem := range strings.Split(s, ".") {
// Look up field; data must be a struct or map.
data = t.lookup(st, data, elem)
if !data.IsValid() {
return reflect.Value{}
}
}
return indirectPtr(data, numStars)
}
// Is there no data to look at?
func empty(v reflect.Value) bool {
v = indirect(v)
if !v.IsValid() {
return true
}
switch v.Kind() {
case reflect.Bool:
return v.Bool() == false
case reflect.String:
return v.String() == ""
case reflect.Struct:
return false
case reflect.Map:
return false
case reflect.Array:
return v.Len() == 0
case reflect.Slice:
return v.Len() == 0
}
return false
}
// Look up a variable or method, up through the parent if necessary.
func (t *Template) varValue(name string, st *state) reflect.Value {
field := t.findVar(st, name)
if !field.IsValid() {
if st.parent == nil {
t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type())
}
return t.varValue(name, st.parent)
}
return field
}
func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) {
fn := t.formatter(fmt)
if fn == nil {
t.execError(st, v.linenum, "missing formatter %s for variable", fmt)
}
fn(wr, fmt, val...)
}
// Evaluate a variable, looking up through the parent if necessary.
// If it has a formatter attached ({var|formatter}) run that too.
func (t *Template) writeVariable(v *variableElement, st *state) {
// Resolve field names
val := make([]interface{}, len(v.args))
for i, arg := range v.args {
if name, ok := arg.(fieldName); ok {
val[i] = t.varValue(string(name), st).Interface()
} else {
val[i] = arg
}
}
for i, fmt := range v.fmts[:len(v.fmts)-1] {
b := &st.buf[i&1]
b.Reset()
t.format(b, fmt, val, v, st)
val = val[0:1]
val[0] = b.Bytes()
}
t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st)
}
// Execute element i. Return next index to execute.
func (t *Template) executeElement(i int, st *state) int {
switch elem := t.elems[i].(type) {
case *textElement:
st.wr.Write(elem.text)
return i + 1
case *literalElement:
st.wr.Write(elem.text)
return i + 1
case *variableElement:
t.writeVariable(elem, st)
return i + 1
case *sectionElement:
t.executeSection(elem, st)
return elem.end
case *repeatedElement:
t.executeRepeated(elem, st)
return elem.end
}
e := t.elems[i]
t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e)
return 0
}
// Execute the template.
func (t *Template) execute(start, end int, st *state) {
for i := start; i < end; {
i = t.executeElement(i, st)
}
}
// Execute a .section
func (t *Template) executeSection(s *sectionElement, st *state) {
// Find driver data for this section. It must be in the current struct.
field := t.varValue(s.field, st)
if !field.IsValid() {
t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type())
}
st = st.clone(field)
start, end := s.start, s.or
if !empty(field) {
// Execute the normal block.
if end < 0 {
end = s.end
}
} else {
// Execute the .or block. If it's missing, do nothing.
start, end = s.or, s.end
if start < 0 {
return
}
}
for i := start; i < end; {
i = t.executeElement(i, st)
}
}
// Return the result of calling the Iter method on v, or nil.
func iter(v reflect.Value) reflect.Value {
for j := 0; j < v.Type().NumMethod(); j++ {
mth := v.Type().Method(j)
fv := v.Method(j)
ft := fv.Type()
// TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue.
if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
continue
}
ct := ft.Out(0)
if ct.Kind() != reflect.Chan ||
ct.ChanDir()&reflect.RecvDir == 0 {
continue
}
return fv.Call(nil)[0]
}
return reflect.Value{}
}
// Execute a .repeated section
func (t *Template) executeRepeated(r *repeatedElement, st *state) {
// Find driver data for this section. It must be in the current struct.
field := t.varValue(r.field, st)
if !field.IsValid() {
t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type())
}
field = indirect(field)
start, end := r.start, r.or
if end < 0 {
end = r.end
}
if r.altstart >= 0 {
end = r.altstart
}
first := true
// Code common to all the loops.
loopBody := func(newst *state) {
// .alternates between elements
if !first && r.altstart >= 0 {
for i := r.altstart; i < r.altend; {
i = t.executeElement(i, newst)
}
}
first = false
for i := start; i < end; {
i = t.executeElement(i, newst)
}
}
if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice {
for j := 0; j < array.Len(); j++ {
loopBody(st.clone(array.Index(j)))
}
} else if m := field; m.Kind() == reflect.Map {
for _, key := range m.MapKeys() {
loopBody(st.clone(m.MapIndex(key)))
}
} else if ch := iter(field); ch.IsValid() {
for {
e, ok := ch.Recv()
if !ok {
break
}
loopBody(st.clone(e))
}
} else {
t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
r.field, field.Type())
}
if first {
// Empty. Execute the .or block, once. If it's missing, do nothing.
start, end := r.or, r.end
if start >= 0 {
newst := st.clone(field)
for i := start; i < end; {
i = t.executeElement(i, newst)
}
}
return
}
}
// A valid delimiter must contain no space and be non-empty.
func validDelim(d []byte) bool {
if len(d) == 0 {
return false
}
for _, c := range d {
if isSpace(c) {
return false
}
}
return true
}
// Copyright 2009 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.
// Template library: default formatters
package template
import (
"bytes"
"fmt"
"io"
)
// StringFormatter formats into the default string representation.
// It is stored under the name "str" and is the default formatter.
// You can override the default formatter by storing your default
// under the name "" in your custom formatter map.
func StringFormatter(w io.Writer, format string, value ...interface{}) {
if len(value) == 1 {
if b, ok := value[0].([]byte); ok {
w.Write(b)
return
}
}
fmt.Fprint(w, value...)
}
var (
esc_quot = []byte("&#34;") // shorter than "&quot;"
esc_apos = []byte("&#39;") // shorter than "&apos;"
esc_amp = []byte("&amp;")
esc_lt = []byte("&lt;")
esc_gt = []byte("&gt;")
)
// HTMLEscape writes to w the properly escaped HTML equivalent
// of the plain text data s.
func HTMLEscape(w io.Writer, s []byte) {
var esc []byte
last := 0
for i, c := range s {
switch c {
case '"':
esc = esc_quot
case '\'':
esc = esc_apos
case '&':
esc = esc_amp
case '<':
esc = esc_lt
case '>':
esc = esc_gt
default:
continue
}
w.Write(s[last:i])
w.Write(esc)
last = i + 1
}
w.Write(s[last:])
}
// HTMLFormatter formats arbitrary values for HTML
func HTMLFormatter(w io.Writer, format string, value ...interface{}) {
ok := false
var b []byte
if len(value) == 1 {
b, ok = value[0].([]byte)
}
if !ok {
var buf bytes.Buffer
fmt.Fprint(&buf, value...)
b = buf.Bytes()
}
HTMLEscape(w, b)
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment