package template
import (
"container/vector"
"fmt"
"io"
"os"
"reflect"
"runtime"
"strings"
)
type Error struct {
Line int
Msg string
}
func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) }
var lbrace = []byte{'{'}
var rbrace = []byte{'}'}
var space = []byte{' '}
var tab = []byte{'\t'}
const (
tokAlternates = iota
tokComment
tokEnd
tokLiteral
tokOr
tokRepeated
tokSection
tokText
tokVariable
)
type FormatterMap map[string]func(io.Writer, interface{}, string)
var builtins = FormatterMap{
"html": HTMLFormatter,
"str": StringFormatter,
"": StringFormatter,
}
type textElement struct {
text []byte
}
type literalElement struct {
text []byte
}
type variableElement struct {
linenum int
name string
formatter string
}
type sectionElement struct {
linenum int
field string
start int
or int
end int
}
type repeatedElement struct {
sectionElement
altstart int
altend int
}
type Template struct {
fmap FormatterMap
ldelim, rdelim []byte
buf []byte
p int
linenum int
error os.Error
elems *vector.Vector
}
type state struct {
parent *state
data reflect.Value
wr io.Writer
errors chan os.Error
}
func (parent *state) clone(data reflect.Value) *state {
return &state{parent, data, parent.wr, parent.errors}
}
func New(fmap FormatterMap) *Template {
t := new(Template)
t.fmap = fmap
t.ldelim = lbrace
t.rdelim = rbrace
t.elems = new(vector.Vector)
return t
}
func (t *Template) execError(st *state, line int, err string, args ...interface{}) {
st.errors <- &Error{line, fmt.Sprintf(err, args)}
runtime.Goexit()
}
func (t *Template) parseError(err string, args ...interface{}) {
t.error = &Error{t.linenum, fmt.Sprintf(err, args)}
}
func white(c uint8) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' }
func equal(s []byte, n int, t []byte) bool {
b := s[n:]
if len(t) > len(b) {
return false
}
for i, c := range t {
if c != b[i] {
return false
}
}
return true
}
func (t *Template) nextItem() []byte {
special := false
trimSpace := t.p == 0 || t.buf[t.p-1] == '\n'
start := t.p
var i int
newline := func() {
t.linenum++
i++
}
for i = start; i < len(t.buf); i++ {
if t.buf[i] == '\n' || !white(t.buf[i]) {
break
}
}
if trimSpace {
start = i
} else if i > start {
t.p = i
return t.buf[start:i]
}
Switch:
switch {
case i == len(t.buf):
case t.buf[i] == '\n':
newline()
case equal(t.buf, i, t.ldelim):
i += len(t.ldelim)
if i+1 < len(t.buf) && (t.buf[i] == '.' || t.buf[i] == '#') {
special = true
}
for ; i < len(t.buf); i++ {
if t.buf[i] == '\n' {
break
}
if equal(t.buf, i, t.rdelim) {
i += len(t.rdelim)
break Switch
}
}
t.parseError("unmatched opening delimiter")
return nil
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]
if special && trimSpace {
for ; i < len(t.buf) && white(t.buf[i]); i++ {
if t.buf[i] == '\n' {
newline()
break
}
}
}
t.p = i
return item
}
func words(buf []byte) []string {
s := make([]string, 0, 5)
p := 0
for i := 0; ; i++ {
for ; p < len(buf) && white(buf[p]); p++ {
}
start := p
for ; p < len(buf) && !white(buf[p]); p++ {
}
if start == p {
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
}
func (t *Template) analyze(item []byte) (tok int, w []string) {
if !equal(item, 0, t.ldelim) {
tok = tokText
return
}
if !equal(item, len(item)-len(t.rdelim), t.rdelim) {
t.parseError("internal error: unmatched opening delimiter")
return
}
if len(item) <= len(t.ldelim)+len(t.rdelim) {
t.parseError("empty directive")
return
}
if item[len(t.ldelim)] == '#' {
tok = tokComment
return
}
w = words(item[len(t.ldelim) : len(item)-len(t.rdelim)])
if len(w) == 0 {
t.parseError("empty directive")
return
}
if len(w) == 1 && w[0][0] != '.' {
tok = tokVariable
return
}
switch w[0] {
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
}
func (t *Template) newVariable(name_formatter string) (v *variableElement) {
name := name_formatter
formatter := ""
bar := strings.Index(name_formatter, "|")
if bar >= 0 {
name = name_formatter[0:bar]
formatter = name_formatter[bar+1:]
}
v = &variableElement{t.linenum, name, formatter}
if t.fmap != nil {
if _, ok := t.fmap[formatter]; ok {
return
}
}
if _, ok := builtins[formatter]; ok {
return
}
t.parseError("unknown formatter: %s", formatter)
return
}
func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
tok, w = t.analyze(item)
if t.error != nil {
return
}
done = true
switch tok {
case tokComment:
return
case tokText:
t.elems.Push(&textElement{item})
return
case tokLiteral:
switch w[0] {
case ".meta-left":
t.elems.Push(&literalElement{t.ldelim})
case ".meta-right":
t.elems.Push(&literalElement{t.rdelim})
case ".space":
t.elems.Push(&literalElement{space})
case ".tab":
t.elems.Push(&literalElement{tab})
default:
t.parseError("internal error: unknown literal: %s", w[0])
return
}
return
case tokVariable:
t.elems.Push(t.newVariable(w[0]))
return
}
return false, tok, w
}
func (t *Template) parseRepeated(words []string) *repeatedElement {
r := new(repeatedElement)
t.elems.Push(r)
r.linenum = t.linenum
r.field = words[2]
r.start = t.elems.Len()
r.or = -1
r.altstart = -1
r.altend = -1
Loop:
for t.error == nil {
item := t.nextItem()
if t.error != nil {
break
}
if len(item) == 0 {
t.parseError("missing .end for .repeated section")
break
}
done, tok, w := t.parseSimple(item)
if t.error != nil {
break
}
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 = t.elems.Len()
r.or = t.elems.Len()
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 = t.elems.Len()
default:
t.parseError("internal error: unknown repeated section item: %s", item)
break Loop
}
}
if t.error != nil {
return nil
}
if r.altend < 0 {
r.altend = t.elems.Len()
}
r.end = t.elems.Len()
return r
}
func (t *Template) parseSection(words []string) *sectionElement {
s := new(sectionElement)
t.elems.Push(s)
s.linenum = t.linenum
s.field = words[1]
s.start = t.elems.Len()
s.or = -1
Loop:
for t.error == nil {
item := t.nextItem()
if t.error != nil {
break
}
if len(item) == 0 {
t.parseError("missing .end for .section")
break
}
done, tok, w := t.parseSimple(item)
if t.error != nil {
break
}
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 = t.elems.Len()
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)
}
}
if t.error != nil {
return nil
}
s.end = t.elems.Len()
return s
}
func (t *Template) parse() {
for t.error == nil {
item := t.nextItem()
if t.error != nil {
break
}
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)
}
}
}
func (st *state) findVar(s string) reflect.Value {
if s == "@" {
return st.data
}
data := st.data
for _, elem := range strings.Split(s, ".", 0) {
origData := data
data = reflect.Indirect(data)
if data == nil {
return nil
}
if intf, ok := data.(*reflect.InterfaceValue); ok {
data = intf.Elem()
}
switch typ := data.Type().(type) {
case *reflect.StructType:
if field, ok := typ.FieldByName(elem); ok {
data = data.(*reflect.StructValue).FieldByIndex(field.Index)
continue
}
case *reflect.MapType:
data = data.(*reflect.MapValue).Elem(reflect.NewValue(elem))
continue
}
if result, found := callMethod(origData, elem); found {
data = result
continue
}
return nil
}
return data
}
func callMethod(data reflect.Value, name string) (result reflect.Value, found bool) {
found = false
for {
typ := data.Type()
if nMethod := data.Type().NumMethod(); nMethod > 0 {
for i := 0; i < nMethod; i++ {
method := typ.Method(i)
if method.Name == name {
found = true
if typ == method.Type.In(0) {
return call(data, method), found
}
}
}
}
if nd, ok := data.(*reflect.PtrValue); ok {
data = nd.Elem()
} else {
break
}
}
return
}
func call(v reflect.Value, method reflect.Method) reflect.Value {
funcType := method.Type
if funcType.NumIn() != 1 {
return nil
}
if funcType.NumOut() != 1 {
return nil
}
return method.Func.Call([]reflect.Value{v})[0]
}
func empty(v reflect.Value) bool {
v = reflect.Indirect(v)
if v == nil {
return true
}
switch v := v.(type) {
case *reflect.BoolValue:
return v.Get() == false
case *reflect.StringValue:
return v.Get() == ""
case *reflect.StructValue:
return false
case *reflect.MapValue:
return false
case *reflect.ArrayValue:
return v.Len() == 0
case *reflect.SliceValue:
return v.Len() == 0
}
return true
}
func (t *Template) varValue(name string, st *state) reflect.Value {
field := st.findVar(name)
if field == nil {
if st.parent == nil {
t.execError(st, t.linenum, "name not found: %s", name)
}
return t.varValue(name, st.parent)
}
if iface, ok := field.(*reflect.InterfaceValue); ok && !iface.IsNil() {
field = iface.Elem()
}
return field
}
func (t *Template) writeVariable(v *variableElement, st *state) {
formatter := v.formatter
val := t.varValue(v.name, st).Interface()
if t.fmap != nil {
if fn, ok := t.fmap[formatter]; ok {
fn(st.wr, val, formatter)
return
}
}
if fn, ok := builtins[formatter]; ok {
fn(st.wr, val, formatter)
return
}
t.execError(st, v.linenum, "missing formatter %s for variable %s", formatter, v.name)
}
func (t *Template) executeElement(i int, st *state) int {
switch elem := t.elems.At(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.At(i)
t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e)
return 0
}
func (t *Template) execute(start, end int, st *state) {
for i := start; i < end; {
i = t.executeElement(i, st)
}
}
func (t *Template) executeSection(s *sectionElement, st *state) {
field := t.varValue(s.field, st)
if field == nil {
t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, reflect.Indirect(st.data).Type())
}
st = st.clone(field)
start, end := s.start, s.or
if !empty(field) {
if end < 0 {
end = s.end
}
} else {
start, end = s.or, s.end
if start < 0 {
return
}
}
for i := start; i < end; {
i = t.executeElement(i, st)
}
}
func iter(v reflect.Value) *reflect.ChanValue {
for j := 0; j < v.Type().NumMethod(); j++ {
mth := v.Type().Method(j)
fv := v.Method(j)
ft := fv.Type().(*reflect.FuncType)
if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 {
continue
}
ct, ok := ft.Out(0).(*reflect.ChanType)
if !ok || ct.Dir()&reflect.RecvDir == 0 {
continue
}
return fv.Call(nil)[0].(*reflect.ChanValue)
}
return nil
}
func (t *Template) executeRepeated(r *repeatedElement, st *state) {
field := t.varValue(r.field, st)
if field == nil {
t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, reflect.Indirect(st.data).Type())
}
start, end := r.start, r.or
if end < 0 {
end = r.end
}
if r.altstart >= 0 {
end = r.altstart
}
first := true
loopBody := func(newst *state) {
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, ok := field.(reflect.ArrayOrSliceValue); ok {
for j := 0; j < array.Len(); j++ {
loopBody(st.clone(array.Elem(j)))
}
} else if m, ok := field.(*reflect.MapValue); ok {
for _, key := range m.Keys() {
loopBody(st.clone(m.Elem(key)))
}
} else if ch := iter(field); ch != nil {
for {
e := ch.Recv()
if ch.Closed() {
break
}
loopBody(st.clone(e))
}
} else {
t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)",
r.field, field.Type())
}
if first {
start, end := r.or, r.end
if start >= 0 {
newst := st.clone(field)
for i := start; i < end; {
i = t.executeElement(i, newst)
}
}
return
}
}
func validDelim(d []byte) bool {
if len(d) == 0 {
return false
}
for _, c := range d {
if white(c) {
return false
}
}
return true
}
func (t *Template) Parse(s string) os.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)}
}
t.buf = []byte(s)
t.p = 0
t.linenum = 1
t.parse()
return t.error
}
func (t *Template) Execute(data interface{}, wr io.Writer) os.Error {
val := reflect.NewValue(data)
errors := make(chan os.Error)
go func() {
t.p = 0
t.execute(0, t.elems.Len(), &state{nil, val, wr, errors})
errors <- nil
}()
return <-errors
}
func (t *Template) SetDelims(left, right string) {
t.ldelim = []byte(left)
t.rdelim = []byte(right)
}
func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) {
t = New(fmap)
err = t.Parse(s)
if err != nil {
t = nil
}
return
}
func MustParse(s string, fmap FormatterMap) *Template {
t, err := Parse(s, fmap)
if err != nil {
panic("template parse error: ", err.String())
}
return t
}