summaryrefslogtreecommitdiff
path: root/libgo/go/old/template/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/old/template/parse.go')
-rw-r--r--libgo/go/old/template/parse.go742
1 files changed, 0 insertions, 742 deletions
diff --git a/libgo/go/old/template/parse.go b/libgo/go/old/template/parse.go
deleted file mode 100644
index e1bfa47249..0000000000
--- a/libgo/go/old/template/parse.go
+++ /dev/null
@@ -1,742 +0,0 @@
-// 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)
-}