mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			743 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			743 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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 parse a template.
 | 
						|
 | 
						|
package template
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"reflect"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"unicode"
 | 
						|
	"unicode/utf8"
 | 
						|
)
 | 
						|
 | 
						|
// Errors returned during parsing and execution.  Users may extract the information and reformat
 | 
						|
// if they desire.
 | 
						|
type Error struct {
 | 
						|
	Line int
 | 
						|
	Msg  string
 | 
						|
}
 | 
						|
 | 
						|
func (e *Error) Error() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) }
 | 
						|
 | 
						|
// checkError is a deferred function to turn a panic with type *Error into a plain error return.
 | 
						|
// Other panics are unexpected and so are re-enabled.
 | 
						|
func checkError(error *error) {
 | 
						|
	if v := recover(); v != nil {
 | 
						|
		if e, ok := v.(*Error); ok {
 | 
						|
			*error = e
 | 
						|
		} else {
 | 
						|
			// runtime errors should crash
 | 
						|
			panic(v)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Most of the literals are aces.
 | 
						|
var lbrace = []byte{'{'}
 | 
						|
var rbrace = []byte{'}'}
 | 
						|
var space = []byte{' '}
 | 
						|
var tab = []byte{'\t'}
 | 
						|
 | 
						|
// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors
 | 
						|
const (
 | 
						|
	tokAlternates = iota
 | 
						|
	tokComment
 | 
						|
	tokEnd
 | 
						|
	tokLiteral
 | 
						|
	tokOr
 | 
						|
	tokRepeated
 | 
						|
	tokSection
 | 
						|
	tokText
 | 
						|
	tokVariable
 | 
						|
)
 | 
						|
 | 
						|
// FormatterMap is the type describing the mapping from formatter
 | 
						|
// names to the functions that implement them.
 | 
						|
type FormatterMap map[string]func(io.Writer, string, ...interface{})
 | 
						|
 | 
						|
// Built-in formatters.
 | 
						|
var builtins = FormatterMap{
 | 
						|
	"html": HTMLFormatter,
 | 
						|
	"str":  StringFormatter,
 | 
						|
	"":     StringFormatter,
 | 
						|
}
 | 
						|
 | 
						|
// The parsed state of a template is a vector of xxxElement structs.
 | 
						|
// Sections have line numbers so errors can be reported better during execution.
 | 
						|
 | 
						|
// Plain text.
 | 
						|
type textElement struct {
 | 
						|
	text []byte
 | 
						|
}
 | 
						|
 | 
						|
// A literal such as .meta-left or .meta-right
 | 
						|
type literalElement struct {
 | 
						|
	text []byte
 | 
						|
}
 | 
						|
 | 
						|
// A variable invocation to be evaluated
 | 
						|
type variableElement struct {
 | 
						|
	linenum int
 | 
						|
	args    []interface{} // The fields and literals in the invocation.
 | 
						|
	fmts    []string      // Names of formatters to apply. len(fmts) > 0
 | 
						|
}
 | 
						|
 | 
						|
// A variableElement arg to be evaluated as a field name
 | 
						|
type fieldName string
 | 
						|
 | 
						|
// A .section block, possibly with a .or
 | 
						|
type sectionElement struct {
 | 
						|
	linenum int    // of .section itself
 | 
						|
	field   string // cursor field for this block
 | 
						|
	start   int    // first element
 | 
						|
	or      int    // first element of .or block
 | 
						|
	end     int    // one beyond last element
 | 
						|
}
 | 
						|
 | 
						|
// A .repeated block, possibly with a .or and a .alternates
 | 
						|
type repeatedElement struct {
 | 
						|
	sectionElement     // It has the same structure...
 | 
						|
	altstart       int // ... except for alternates
 | 
						|
	altend         int
 | 
						|
}
 | 
						|
 | 
						|
// Template is the type that represents a template definition.
 | 
						|
// It is unchanged after parsing.
 | 
						|
type Template struct {
 | 
						|
	fmap FormatterMap // formatters for variables
 | 
						|
	// Used during parsing:
 | 
						|
	ldelim, rdelim []byte // delimiters; default {}
 | 
						|
	buf            []byte // input text to process
 | 
						|
	p              int    // position in buf
 | 
						|
	linenum        int    // position in input
 | 
						|
	// Parsed results:
 | 
						|
	elems []interface{}
 | 
						|
}
 | 
						|
 | 
						|
// New creates a new template with the specified formatter map (which
 | 
						|
// may be nil) to define auxiliary functions for formatting variables.
 | 
						|
func New(fmap FormatterMap) *Template {
 | 
						|
	t := new(Template)
 | 
						|
	t.fmap = fmap
 | 
						|
	t.ldelim = lbrace
 | 
						|
	t.rdelim = rbrace
 | 
						|
	t.elems = make([]interface{}, 0, 16)
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// Report error and stop executing.  The line number must be provided explicitly.
 | 
						|
func (t *Template) execError(st *state, line int, err string, args ...interface{}) {
 | 
						|
	panic(&Error{line, fmt.Sprintf(err, args...)})
 | 
						|
}
 | 
						|
 | 
						|
// Report error, panic to terminate parsing.
 | 
						|
// The line number comes from the template state.
 | 
						|
func (t *Template) parseError(err string, args ...interface{}) {
 | 
						|
	panic(&Error{t.linenum, fmt.Sprintf(err, args...)})
 | 
						|
}
 | 
						|
 | 
						|
// Is this an exported - upper case - name?
 | 
						|
func isExported(name string) bool {
 | 
						|
	r, _ := utf8.DecodeRuneInString(name)
 | 
						|
	return unicode.IsUpper(r)
 | 
						|
}
 | 
						|
 | 
						|
// -- Lexical analysis
 | 
						|
 | 
						|
// Is c a space character?
 | 
						|
func isSpace(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' }
 | 
						|
 | 
						|
// Safely, does s[n:n+len(t)] == t?
 | 
						|
func equal(s []byte, n int, t []byte) bool {
 | 
						|
	b := s[n:]
 | 
						|
	if len(t) > len(b) { // not enough space left for a match.
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	for i, c := range t {
 | 
						|
		if c != b[i] {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// isQuote returns true if c is a string- or character-delimiting quote character.
 | 
						|
func isQuote(c byte) bool {
 | 
						|
	return c == '"' || c == '`' || c == '\''
 | 
						|
}
 | 
						|
 | 
						|
// endQuote returns the end quote index for the quoted string that
 | 
						|
// starts at n, or -1 if no matching end quote is found before the end
 | 
						|
// of the line.
 | 
						|
func endQuote(s []byte, n int) int {
 | 
						|
	quote := s[n]
 | 
						|
	for n++; n < len(s); n++ {
 | 
						|
		switch s[n] {
 | 
						|
		case '\\':
 | 
						|
			if quote == '"' || quote == '\'' {
 | 
						|
				n++
 | 
						|
			}
 | 
						|
		case '\n':
 | 
						|
			return -1
 | 
						|
		case quote:
 | 
						|
			return n
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return -1
 | 
						|
}
 | 
						|
 | 
						|
// nextItem returns the next item from the input buffer.  If the returned
 | 
						|
// item is empty, we are at EOF.  The item will be either a
 | 
						|
// delimited string or a non-empty string between delimited
 | 
						|
// strings. Tokens stop at (but include, if plain text) a newline.
 | 
						|
// Action tokens on a line by themselves drop any space on
 | 
						|
// either side, up to and including the newline.
 | 
						|
func (t *Template) nextItem() []byte {
 | 
						|
	startOfLine := t.p == 0 || t.buf[t.p-1] == '\n'
 | 
						|
	start := t.p
 | 
						|
	var i int
 | 
						|
	newline := func() {
 | 
						|
		t.linenum++
 | 
						|
		i++
 | 
						|
	}
 | 
						|
	// Leading space up to but not including newline
 | 
						|
	for i = start; i < len(t.buf); i++ {
 | 
						|
		if t.buf[i] == '\n' || !isSpace(t.buf[i]) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	leadingSpace := i > start
 | 
						|
	// What's left is nothing, newline, delimited string, or plain text
 | 
						|
	switch {
 | 
						|
	case i == len(t.buf):
 | 
						|
		// EOF; nothing to do
 | 
						|
	case t.buf[i] == '\n':
 | 
						|
		newline()
 | 
						|
	case equal(t.buf, i, t.ldelim):
 | 
						|
		left := i         // Start of left delimiter.
 | 
						|
		right := -1       // Will be (immediately after) right delimiter.
 | 
						|
		haveText := false // Delimiters contain text.
 | 
						|
		i += len(t.ldelim)
 | 
						|
		// Find the end of the action.
 | 
						|
		for ; i < len(t.buf); i++ {
 | 
						|
			if t.buf[i] == '\n' {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if isQuote(t.buf[i]) {
 | 
						|
				i = endQuote(t.buf, i)
 | 
						|
				if i == -1 {
 | 
						|
					t.parseError("unmatched quote")
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if equal(t.buf, i, t.rdelim) {
 | 
						|
				i += len(t.rdelim)
 | 
						|
				right = i
 | 
						|
				break
 | 
						|
			}
 | 
						|
			haveText = true
 | 
						|
		}
 | 
						|
		if right < 0 {
 | 
						|
			t.parseError("unmatched opening delimiter")
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		// Is this a special action (starts with '.' or '#') and the only thing on the line?
 | 
						|
		if startOfLine && haveText {
 | 
						|
			firstChar := t.buf[left+len(t.ldelim)]
 | 
						|
			if firstChar == '.' || firstChar == '#' {
 | 
						|
				// It's special and the first thing on the line. Is it the last?
 | 
						|
				for j := right; j < len(t.buf) && isSpace(t.buf[j]); j++ {
 | 
						|
					if t.buf[j] == '\n' {
 | 
						|
						// Yes it is. Drop the surrounding space and return the {.foo}
 | 
						|
						t.linenum++
 | 
						|
						t.p = j + 1
 | 
						|
						return t.buf[left:right]
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// No it's not. If there's leading space, return that.
 | 
						|
		if leadingSpace {
 | 
						|
			// not trimming space: return leading space if there is some.
 | 
						|
			t.p = left
 | 
						|
			return t.buf[start:left]
 | 
						|
		}
 | 
						|
		// Return the word, leave the trailing space.
 | 
						|
		start = left
 | 
						|
		break
 | 
						|
	default:
 | 
						|
		for ; i < len(t.buf); i++ {
 | 
						|
			if t.buf[i] == '\n' {
 | 
						|
				newline()
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if equal(t.buf, i, t.ldelim) {
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	item := t.buf[start:i]
 | 
						|
	t.p = i
 | 
						|
	return item
 | 
						|
}
 | 
						|
 | 
						|
// Turn a byte array into a space-split array of strings,
 | 
						|
// taking into account quoted strings.
 | 
						|
func words(buf []byte) []string {
 | 
						|
	s := make([]string, 0, 5)
 | 
						|
	for i := 0; i < len(buf); {
 | 
						|
		// One word per loop
 | 
						|
		for i < len(buf) && isSpace(buf[i]) {
 | 
						|
			i++
 | 
						|
		}
 | 
						|
		if i == len(buf) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		// Got a word
 | 
						|
		start := i
 | 
						|
		if isQuote(buf[i]) {
 | 
						|
			i = endQuote(buf, i)
 | 
						|
			if i < 0 {
 | 
						|
				i = len(buf)
 | 
						|
			} else {
 | 
						|
				i++
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Even with quotes, break on space only.  This handles input
 | 
						|
		// such as {""|} and catches quoting mistakes.
 | 
						|
		for i < len(buf) && !isSpace(buf[i]) {
 | 
						|
			i++
 | 
						|
		}
 | 
						|
		s = append(s, string(buf[start:i]))
 | 
						|
	}
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// Analyze an item and return its token type and, if it's an action item, an array of
 | 
						|
// its constituent words.
 | 
						|
func (t *Template) analyze(item []byte) (tok int, w []string) {
 | 
						|
	// item is known to be non-empty
 | 
						|
	if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter
 | 
						|
		tok = tokText
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter
 | 
						|
		t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents
 | 
						|
		t.parseError("empty directive")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// Comment
 | 
						|
	if item[len(t.ldelim)] == '#' {
 | 
						|
		tok = tokComment
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// Split into words
 | 
						|
	w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)]) // drop final delimiter
 | 
						|
	if len(w) == 0 {
 | 
						|
		t.parseError("empty directive")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	first := w[0]
 | 
						|
	if first[0] != '.' {
 | 
						|
		tok = tokVariable
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(first) > 1 && first[1] >= '0' && first[1] <= '9' {
 | 
						|
		// Must be a float.
 | 
						|
		tok = tokVariable
 | 
						|
		return
 | 
						|
	}
 | 
						|
	switch first {
 | 
						|
	case ".meta-left", ".meta-right", ".space", ".tab":
 | 
						|
		tok = tokLiteral
 | 
						|
		return
 | 
						|
	case ".or":
 | 
						|
		tok = tokOr
 | 
						|
		return
 | 
						|
	case ".end":
 | 
						|
		tok = tokEnd
 | 
						|
		return
 | 
						|
	case ".section":
 | 
						|
		if len(w) != 2 {
 | 
						|
			t.parseError("incorrect fields for .section: %s", item)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		tok = tokSection
 | 
						|
		return
 | 
						|
	case ".repeated":
 | 
						|
		if len(w) != 3 || w[1] != "section" {
 | 
						|
			t.parseError("incorrect fields for .repeated: %s", item)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		tok = tokRepeated
 | 
						|
		return
 | 
						|
	case ".alternates":
 | 
						|
		if len(w) != 2 || w[1] != "with" {
 | 
						|
			t.parseError("incorrect fields for .alternates: %s", item)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		tok = tokAlternates
 | 
						|
		return
 | 
						|
	}
 | 
						|
	t.parseError("bad directive: %s", item)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// formatter returns the Formatter with the given name in the Template, or nil if none exists.
 | 
						|
func (t *Template) formatter(name string) func(io.Writer, string, ...interface{}) {
 | 
						|
	if t.fmap != nil {
 | 
						|
		if fn := t.fmap[name]; fn != nil {
 | 
						|
			return fn
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return builtins[name]
 | 
						|
}
 | 
						|
 | 
						|
// -- Parsing
 | 
						|
 | 
						|
// newVariable allocates a new variable-evaluation element.
 | 
						|
func (t *Template) newVariable(words []string) *variableElement {
 | 
						|
	formatters := extractFormatters(words)
 | 
						|
	args := make([]interface{}, len(words))
 | 
						|
 | 
						|
	// Build argument list, processing any literals
 | 
						|
	for i, word := range words {
 | 
						|
		var lerr error
 | 
						|
		switch word[0] {
 | 
						|
		case '"', '`', '\'':
 | 
						|
			v, err := strconv.Unquote(word)
 | 
						|
			if err == nil && word[0] == '\'' {
 | 
						|
				args[i], _ = utf8.DecodeRuneInString(v)
 | 
						|
			} else {
 | 
						|
				args[i], lerr = v, err
 | 
						|
			}
 | 
						|
 | 
						|
		case '.', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
 | 
						|
			v, err := strconv.ParseInt(word, 0, 64)
 | 
						|
			if err == nil {
 | 
						|
				args[i] = v
 | 
						|
			} else {
 | 
						|
				v, err := strconv.ParseFloat(word, 64)
 | 
						|
				args[i], lerr = v, err
 | 
						|
			}
 | 
						|
 | 
						|
		default:
 | 
						|
			args[i] = fieldName(word)
 | 
						|
		}
 | 
						|
		if lerr != nil {
 | 
						|
			t.parseError("invalid literal: %q: %s", word, lerr)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// We could remember the function address here and avoid the lookup later,
 | 
						|
	// but it's more dynamic to let the user change the map contents underfoot.
 | 
						|
	// We do require the name to be present, though.
 | 
						|
 | 
						|
	// Is it in user-supplied map?
 | 
						|
	for _, f := range formatters {
 | 
						|
		if t.formatter(f) == nil {
 | 
						|
			t.parseError("unknown formatter: %q", f)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return &variableElement{t.linenum, args, formatters}
 | 
						|
}
 | 
						|
 | 
						|
// extractFormatters extracts a list of formatters from words.
 | 
						|
// After the final space-separated argument in a variable, formatters may be
 | 
						|
// specified separated by pipe symbols. For example: {a b c|d|e}
 | 
						|
// The words parameter still has the formatters joined by '|' in the last word.
 | 
						|
// extractFormatters splits formatters, replaces the last word with the content
 | 
						|
// found before the first '|' within it, and returns the formatters obtained.
 | 
						|
// If no formatters are found in words, the default formatter is returned.
 | 
						|
func extractFormatters(words []string) (formatters []string) {
 | 
						|
	// "" is the default formatter.
 | 
						|
	formatters = []string{""}
 | 
						|
	if len(words) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	var bar int
 | 
						|
	lastWord := words[len(words)-1]
 | 
						|
	if isQuote(lastWord[0]) {
 | 
						|
		end := endQuote([]byte(lastWord), 0)
 | 
						|
		if end < 0 || end+1 == len(lastWord) || lastWord[end+1] != '|' {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		bar = end + 1
 | 
						|
	} else {
 | 
						|
		bar = strings.IndexRune(lastWord, '|')
 | 
						|
		if bar < 0 {
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	words[len(words)-1] = lastWord[0:bar]
 | 
						|
	formatters = strings.Split(lastWord[bar+1:], "|")
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// Grab the next item.  If it's simple, just append it to the template.
 | 
						|
// Otherwise return its details.
 | 
						|
func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
 | 
						|
	tok, w = t.analyze(item)
 | 
						|
	done = true // assume for simplicity
 | 
						|
	switch tok {
 | 
						|
	case tokComment:
 | 
						|
		return
 | 
						|
	case tokText:
 | 
						|
		t.elems = append(t.elems, &textElement{item})
 | 
						|
		return
 | 
						|
	case tokLiteral:
 | 
						|
		switch w[0] {
 | 
						|
		case ".meta-left":
 | 
						|
			t.elems = append(t.elems, &literalElement{t.ldelim})
 | 
						|
		case ".meta-right":
 | 
						|
			t.elems = append(t.elems, &literalElement{t.rdelim})
 | 
						|
		case ".space":
 | 
						|
			t.elems = append(t.elems, &literalElement{space})
 | 
						|
		case ".tab":
 | 
						|
			t.elems = append(t.elems, &literalElement{tab})
 | 
						|
		default:
 | 
						|
			t.parseError("internal error: unknown literal: %s", w[0])
 | 
						|
		}
 | 
						|
		return
 | 
						|
	case tokVariable:
 | 
						|
		t.elems = append(t.elems, t.newVariable(w))
 | 
						|
		return
 | 
						|
	}
 | 
						|
	return false, tok, w
 | 
						|
}
 | 
						|
 | 
						|
// parseRepeated and parseSection are mutually recursive
 | 
						|
 | 
						|
func (t *Template) parseRepeated(words []string) *repeatedElement {
 | 
						|
	r := new(repeatedElement)
 | 
						|
	t.elems = append(t.elems, r)
 | 
						|
	r.linenum = t.linenum
 | 
						|
	r.field = words[2]
 | 
						|
	// Scan section, collecting true and false (.or) blocks.
 | 
						|
	r.start = len(t.elems)
 | 
						|
	r.or = -1
 | 
						|
	r.altstart = -1
 | 
						|
	r.altend = -1
 | 
						|
Loop:
 | 
						|
	for {
 | 
						|
		item := t.nextItem()
 | 
						|
		if len(item) == 0 {
 | 
						|
			t.parseError("missing .end for .repeated section")
 | 
						|
			break
 | 
						|
		}
 | 
						|
		done, tok, w := t.parseSimple(item)
 | 
						|
		if done {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		switch tok {
 | 
						|
		case tokEnd:
 | 
						|
			break Loop
 | 
						|
		case tokOr:
 | 
						|
			if r.or >= 0 {
 | 
						|
				t.parseError("extra .or in .repeated section")
 | 
						|
				break Loop
 | 
						|
			}
 | 
						|
			r.altend = len(t.elems)
 | 
						|
			r.or = len(t.elems)
 | 
						|
		case tokSection:
 | 
						|
			t.parseSection(w)
 | 
						|
		case tokRepeated:
 | 
						|
			t.parseRepeated(w)
 | 
						|
		case tokAlternates:
 | 
						|
			if r.altstart >= 0 {
 | 
						|
				t.parseError("extra .alternates in .repeated section")
 | 
						|
				break Loop
 | 
						|
			}
 | 
						|
			if r.or >= 0 {
 | 
						|
				t.parseError(".alternates inside .or block in .repeated section")
 | 
						|
				break Loop
 | 
						|
			}
 | 
						|
			r.altstart = len(t.elems)
 | 
						|
		default:
 | 
						|
			t.parseError("internal error: unknown repeated section item: %s", item)
 | 
						|
			break Loop
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if r.altend < 0 {
 | 
						|
		r.altend = len(t.elems)
 | 
						|
	}
 | 
						|
	r.end = len(t.elems)
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func (t *Template) parseSection(words []string) *sectionElement {
 | 
						|
	s := new(sectionElement)
 | 
						|
	t.elems = append(t.elems, s)
 | 
						|
	s.linenum = t.linenum
 | 
						|
	s.field = words[1]
 | 
						|
	// Scan section, collecting true and false (.or) blocks.
 | 
						|
	s.start = len(t.elems)
 | 
						|
	s.or = -1
 | 
						|
Loop:
 | 
						|
	for {
 | 
						|
		item := t.nextItem()
 | 
						|
		if len(item) == 0 {
 | 
						|
			t.parseError("missing .end for .section")
 | 
						|
			break
 | 
						|
		}
 | 
						|
		done, tok, w := t.parseSimple(item)
 | 
						|
		if done {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		switch tok {
 | 
						|
		case tokEnd:
 | 
						|
			break Loop
 | 
						|
		case tokOr:
 | 
						|
			if s.or >= 0 {
 | 
						|
				t.parseError("extra .or in .section")
 | 
						|
				break Loop
 | 
						|
			}
 | 
						|
			s.or = len(t.elems)
 | 
						|
		case tokSection:
 | 
						|
			t.parseSection(w)
 | 
						|
		case tokRepeated:
 | 
						|
			t.parseRepeated(w)
 | 
						|
		case tokAlternates:
 | 
						|
			t.parseError(".alternates not in .repeated")
 | 
						|
		default:
 | 
						|
			t.parseError("internal error: unknown section item: %s", item)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	s.end = len(t.elems)
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
func (t *Template) parse() {
 | 
						|
	for {
 | 
						|
		item := t.nextItem()
 | 
						|
		if len(item) == 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		done, tok, w := t.parseSimple(item)
 | 
						|
		if done {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		switch tok {
 | 
						|
		case tokOr, tokEnd, tokAlternates:
 | 
						|
			t.parseError("unexpected %s", w[0])
 | 
						|
		case tokSection:
 | 
						|
			t.parseSection(w)
 | 
						|
		case tokRepeated:
 | 
						|
			t.parseRepeated(w)
 | 
						|
		default:
 | 
						|
			t.parseError("internal error: bad directive in parse: %s", item)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// -- Execution
 | 
						|
 | 
						|
// -- Public interface
 | 
						|
 | 
						|
// Parse initializes a Template by parsing its definition.  The string
 | 
						|
// s contains the template text.  If any errors occur, Parse returns
 | 
						|
// the error.
 | 
						|
func (t *Template) Parse(s string) (err error) {
 | 
						|
	if t.elems == nil {
 | 
						|
		return &Error{1, "template not allocated with New"}
 | 
						|
	}
 | 
						|
	if !validDelim(t.ldelim) || !validDelim(t.rdelim) {
 | 
						|
		return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)}
 | 
						|
	}
 | 
						|
	defer checkError(&err)
 | 
						|
	t.buf = []byte(s)
 | 
						|
	t.p = 0
 | 
						|
	t.linenum = 1
 | 
						|
	t.parse()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ParseFile is like Parse but reads the template definition from the
 | 
						|
// named file.
 | 
						|
func (t *Template) ParseFile(filename string) (err error) {
 | 
						|
	b, err := ioutil.ReadFile(filename)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return t.Parse(string(b))
 | 
						|
}
 | 
						|
 | 
						|
// Execute applies a parsed template to the specified data object,
 | 
						|
// generating output to wr.
 | 
						|
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
 | 
						|
	// Extract the driver data.
 | 
						|
	val := reflect.ValueOf(data)
 | 
						|
	defer checkError(&err)
 | 
						|
	t.p = 0
 | 
						|
	t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr})
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// SetDelims sets the left and right delimiters for operations in the
 | 
						|
// template.  They are validated during parsing.  They could be
 | 
						|
// validated here but it's better to keep the routine simple.  The
 | 
						|
// delimiters are very rarely invalid and Parse has the necessary
 | 
						|
// error-handling interface already.
 | 
						|
func (t *Template) SetDelims(left, right string) {
 | 
						|
	t.ldelim = []byte(left)
 | 
						|
	t.rdelim = []byte(right)
 | 
						|
}
 | 
						|
 | 
						|
// Parse creates a Template with default parameters (such as {} for
 | 
						|
// metacharacters).  The string s contains the template text while
 | 
						|
// the formatter map fmap, which may be nil, defines auxiliary functions
 | 
						|
// for formatting variables.  The template is returned. If any errors
 | 
						|
// occur, err will be non-nil.
 | 
						|
func Parse(s string, fmap FormatterMap) (t *Template, err error) {
 | 
						|
	t = New(fmap)
 | 
						|
	err = t.Parse(s)
 | 
						|
	if err != nil {
 | 
						|
		t = nil
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// ParseFile is a wrapper function that creates a Template with default
 | 
						|
// parameters (such as {} for metacharacters).  The filename identifies
 | 
						|
// a file containing the template text, while the formatter map fmap, which
 | 
						|
// may be nil, defines auxiliary functions for formatting variables.
 | 
						|
// The template is returned. If any errors occur, err will be non-nil.
 | 
						|
func ParseFile(filename string, fmap FormatterMap) (t *Template, err error) {
 | 
						|
	b, err := ioutil.ReadFile(filename)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return Parse(string(b), fmap)
 | 
						|
}
 | 
						|
 | 
						|
// MustParse is like Parse but panics if the template cannot be parsed.
 | 
						|
func MustParse(s string, fmap FormatterMap) *Template {
 | 
						|
	t, err := Parse(s, fmap)
 | 
						|
	if err != nil {
 | 
						|
		panic("template.MustParse error: " + err.Error())
 | 
						|
	}
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// MustParseFile is like ParseFile but panics if the file cannot be read
 | 
						|
// or the template cannot be parsed.
 | 
						|
func MustParseFile(filename string, fmap FormatterMap) *Template {
 | 
						|
	b, err := ioutil.ReadFile(filename)
 | 
						|
	if err != nil {
 | 
						|
		panic("template.MustParseFile error: " + err.Error())
 | 
						|
	}
 | 
						|
	return MustParse(string(b), fmap)
 | 
						|
}
 |