// 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. /* Data-driven templates for generating textual output such as HTML. See http://code.google.com/p/json-template/wiki/Reference for full documentation of the template language. A summary: 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) 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. Major constructs ({} are metacharacters; [] marks 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. (TODO(r): .alternates is not yet implemented) {field} {field|formatter} Insert the value of the field into the output. 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 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.Write, data interface{}, formatter string) where wr is the destination for output, data is the field value, and formatter is its name at the invocation site. */ package template import ( "fmt"; "io"; "os"; "reflect"; "strings"; "template"; ) // Errors returned during parsing. TODO: different error model for execution? type ParseError struct { os.ErrorString } // All the literals are aces. var lbrace = []byte{ '{' } var rbrace = []byte{ '}' } var space = []byte{ ' ' } // The various types of "tokens", which are plain text or (usually) brace-delimited descriptors const ( Alternates = iota; Comment; End; Literal; Or; Repeated; Section; Text; Variable; ) // FormatterMap is the type describing the mapping from formatter // names to the functions that implement them. type FormatterMap map[string] func(io.Write, interface{}, string) // Built-in formatters. var builtins = FormatterMap { "html" : HtmlFormatter, "str" : StringFormatter, "" : StringFormatter, } // State for executing a Template type state struct { parent *state; // parent in hierarchy errorchan chan os.Error; // for erroring out data reflect.Value; // the driver data for this section etc. wr io.Write; // where to send output } // Report error and stop generation. func (st *state) parseError(line int, err string, args ...) { st.errorchan <- ParseError{fmt.Sprintf("line %d: %s", line, fmt.Sprintf(err, args))}; sys.Goexit(); } // Template is the type that represents a template definition. type Template struct { fmap FormatterMap; // formatters for variables ldelim, rdelim []byte; // delimiters; default {} buf []byte; // input text to process p int; // position in buf linenum *int; // position in input } // Initialize a top-level template in prepratation for parsing. // The formatter map and delimiters are already set. func (t *Template) init(buf []byte) *Template { t.buf = buf; t.p = 0; t.linenum = new(int); return t; } // Create a template deriving from its parent func childTemplate(parent *Template, buf []byte) *Template { t := new(Template); t.ldelim = parent.ldelim; t.rdelim = parent.rdelim; t.buf = buf; t.p = 0; t.fmap = parent.fmap; t.linenum = parent.linenum; return t; } // Is c a white space character? func white(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:len(s)]; 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 } func (t *Template) execute(st *state) func (t *Template) executeSection(w []string, st *state) // 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 the white space on // either side, up to and including the newline. func (t *Template) nextItem(st *state) []byte { sawLeft := false; // are we waiting for an opening delimiter? special := false; // is this a {.foo} directive, which means trim white space? // Delete surrounding white space if this {.foo} is the only thing on the line. trim_white := t.p == 0 || t.buf[t.p-1] == '\n'; only_white := true; // we have seen only white space so far var i int; start := t.p; Loop: for i = t.p; i < len(t.buf); i++ { switch { case t.buf[i] == '\n': *t.linenum++; i++; break Loop; case white(t.buf[i]): // white space, do nothing case !sawLeft && equal(t.buf, i, t.ldelim): // sawLeft checked because delims may be equal // anything interesting already on the line? if !only_white { break Loop; } // is it a directive or comment? j := i + len(t.ldelim); // position after delimiter if j+1 < len(t.buf) && (t.buf[j] == '.' || t.buf[j] == '#') { special = true; if trim_white && only_white { start = i; } } else if i > t.p { // have some text accumulated so stop before delimiter break Loop; } sawLeft = true; i = j - 1; case equal(t.buf, i, t.rdelim): if !sawLeft { st.parseError(*t.linenum, "unmatched closing delimiter") } sawLeft = false; i += len(t.rdelim); break Loop; default: only_white = false; } } if sawLeft { st.parseError(*t.linenum, "unmatched opening delimiter") } item := t.buf[start:i]; if special && trim_white { // consume trailing white space for ; i < len(t.buf) && white(t.buf[i]); i++ { if t.buf[i] == '\n' { i++; break // stop after newline } } } t.p = i; return item } // Turn a byte array into a white-space-split array of strings. func words(buf []byte) []string { s := make([]string, 0, 5); p := 0; // position in buf // one word per loop for i := 0; ; i++ { // skip white space for ; p < len(buf) && white(buf[p]); p++ { } // grab word start := p; for ; p < len(buf) && !white(buf[p]); p++ { } if start == p { // no text left break } if i == cap(s) { ns := make([]string, 2*cap(s)); for j := range s { ns[j] = s[j] } s = ns; } s = s[0:i+1]; s[i] = string(buf[start:p]) } return s } // Analyze an item and return its type and, if it's an action item, an array of // its constituent words. func (t *Template) analyze(item []byte, st *state) (tok int, w []string) { // item is known to be non-empty if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter tok = Text; return } if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter st.parseError(*t.linenum, "unmatched opening delimiter") // should not happen anyway } if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents st.parseError(*t.linenum, "empty directive") } // Comment if item[len(t.ldelim)] == '#' { tok = Comment; return } // Split into words w = words(item[len(t.ldelim): len(item)-len(t.rdelim)]); // drop final delimiter if len(w) == 0 { st.parseError(*t.linenum, "empty directive") } if len(w) == 1 && w[0][0] != '.' { tok = Variable; return; } switch w[0] { case ".meta-left", ".meta-right", ".space": tok = Literal; return; case ".or": tok = Or; return; case ".end": tok = End; return; case ".section": if len(w) != 2 { st.parseError(*t.linenum, "incorrect fields for .section: %s", item) } tok = Section; return; case ".repeated": if len(w) != 3 || w[1] != "section" { st.parseError(*t.linenum, "incorrect fields for .repeated: %s", item) } tok = Repeated; return; case ".alternates": if len(w) != 2 || w[1] != "with" { st.parseError(*t.linenum, "incorrect fields for .alternates: %s", item) } tok = Alternates; return; } st.parseError(*t.linenum, "bad directive: %s", item); return } // If the data for this template is a struct, find the named variable. // The special name "@" denotes the current data. func (st *state) findVar(s string) reflect.Value { if s == "@" { return st.data } data := reflect.Indirect(st.data); typ, ok := data.Type().(reflect.StructType); if ok { for i := 0; i < typ.Len(); i++ { name, ftyp, tag, offset := typ.Field(i); if name == s { return data.(reflect.StructValue).Field(i) } } } return nil } // Is there no data to look at? func empty(v reflect.Value, indirect_ok bool) bool { v = reflect.Indirect(v); if v == nil { return true } switch v.Type().Kind() { case reflect.StringKind: return v.(reflect.StringValue).Get() == ""; case reflect.StructKind: return false; case reflect.ArrayKind: return v.(reflect.ArrayValue).Len() == 0; } return true; } // Execute a ".repeated" section func (t *Template) executeRepeated(w []string, st *state) { if w[1] != "section" { st.parseError(*t.linenum, `.repeated must have "section"`) } // Find driver array/struct for this section. It must be in the current struct. field := st.findVar(w[2]); if field == nil { st.parseError(*t.linenum, ".repeated: cannot find %s in %s", w[2], reflect.Indirect(st.data).Type()); } field = reflect.Indirect(field); // Must be an array/slice if field != nil && field.Kind() != reflect.ArrayKind { st.parseError(*t.linenum, ".repeated: %s has bad type %s", w[2], field.Type()); } // Scan repeated section, remembering slice of text we must execute. nesting := 0; start := t.p; end := t.p; Loop: for { item := t.nextItem(st); if len(item) == 0 { st.parseError(*t.linenum, "missing .end") } tok, s := t.analyze(item, st); switch tok { case Comment: continue; // just ignore it case End: if nesting == 0 { break Loop } nesting--; case Repeated, Section: nesting++; case Literal, Or, Text, Variable: // just accumulate default: panic("unknown section item", string(item)); } end = t.p } if field != nil { array := field.(reflect.ArrayValue); for i := 0; i < array.Len(); i++ { tmp := childTemplate(t, t.buf[start:end]); tmp.execute(&state{st, st.errorchan, array.Elem(i), st.wr}); } } } // Execute a ".section" func (t *Template) executeSection(w []string, st *state) { // Find driver data for this section. It must be in the current struct. field := st.findVar(w[1]); if field == nil { st.parseError(*t.linenum, ".section: cannot find %s in %s", w[1], reflect.Indirect(st.data).Type()); } // Scan section, remembering slice of text we must execute. orFound := false; nesting := 0; // How deeply are .section and .repeated nested? start := t.p; end := t.p; accumulate := !empty(field, true); // Keep this section if there's data Loop: for { item := t.nextItem(st); if len(item) == 0 { st.parseError(*t.linenum, "missing .end") } tok, s := t.analyze(item, st); switch tok { case Comment: continue; // just ignore it case End: if nesting == 0 { break Loop } nesting--; case Or: if nesting > 0 { // just accumulate break } if orFound { st.parseError(*t.linenum, "unexpected .or"); } orFound = true; if !accumulate { // No data; execute the .or instead start = t.p; end = t.p; accumulate = true; continue; } else { // Data present so disregard the .or section accumulate = false } case Repeated, Section: nesting++; case Literal, Text, Variable: // just accumulate default: panic("unknown section item", string(item)); } if accumulate { end = t.p } } tmp := childTemplate(t, t.buf[start:end]); tmp.execute(&state{st, st.errorchan, field, st.wr}); } // Look up a variable, up through the parent if necessary. func (t *Template) varValue(name string, st *state) reflect.Value { field := st.findVar(name); if field == nil { if st.parent == nil { st.parseError(*t.linenum, "name not found: %s", name) } return t.varValue(name, st.parent); } return field; } // 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(st *state, name_formatter string) { name := name_formatter; formatter := ""; bar := strings.Index(name_formatter, "|"); if bar >= 0 { name = name_formatter[0:bar]; formatter = name_formatter[bar+1:len(name_formatter)]; } val := t.varValue(name, st).Interface(); // is it in user-supplied map? if t.fmap != nil { if fn, ok := t.fmap[formatter]; ok { fn(st.wr, val, formatter); return; } } // is it in builtin map? if fn, ok := builtins[formatter]; ok { fn(st.wr, val, formatter); return; } st.parseError(*t.linenum, "unknown formatter: %s", formatter); panic("notreached"); } // Execute the template. execute, executeSection and executeRepeated // are mutually recursive. func (t *Template) execute(st *state) { for { item := t.nextItem(st); if len(item) == 0 { return } tok, w := t.analyze(item, st); switch tok { case Comment: break; case Text: st.wr.Write(item); case Literal: switch w[0] { case ".meta-left": st.wr.Write(t.ldelim); case ".meta-right": st.wr.Write(t.rdelim); case ".space": st.wr.Write(space); default: panic("unknown literal: ", w[0]); } case Variable: t.writeVariable(st, w[0]); case Or, End, Alternates: st.parseError(*t.linenum, "unexpected %s", w[0]); case Section: t.executeSection(w, st); case Repeated: t.executeRepeated(w, st); default: panic("bad directive in execute:", string(item)); } } } func (t *Template) doParse() { // stub for now } // A valid delimiter must contain no white space and be non-empty. func validDelim(d []byte) bool { if len(d) == 0 { return false } for i, c := range d { if white(c) { return false } } return true; } // 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 os.Error) { if !validDelim(t.ldelim) || !validDelim(t.rdelim) { return ParseError{fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} } t.init(io.StringBytes(s)); ch := make(chan os.Error); go func() { t.doParse(); ch <- nil; // clean return; }(); err = <-ch; return } // Execute executes a parsed template on the specified data object, // generating output to wr. func (t *Template) Execute(data interface{}, wr io.Write) os.Error { // Extract the driver data. val := reflect.NewValue(data); ch := make(chan os.Error); go func() { t.p = 0; t.execute(&state{nil, ch, val, wr}); ch <- nil; // clean return; }(); return <-ch; } // New creates a new template with the specified formatter map (which // may be nil) defining auxiliary functions for formatting variables. func New(fmap FormatterMap) *Template { t := new(Template); t.fmap = fmap; t.ldelim = lbrace; t.rdelim = rbrace; return t; } // 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 = io.StringBytes(left); t.rdelim = io.StringBytes(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 os.Error) { t = New(fmap); err = t.Parse(s); return }