func lookup(contextChain []interface{}, name string, allowMissing bool) (reflect.Value, error) {
	if name != "." && strings.Contains(name, ".") {
		parts := strings.SplitN(name, ".", 2)
		v, err := lookup(contextChain, parts[0], allowMissing)
		if err != nil {
			return v, err
		}
		return lookup([]interface{}{v}, parts[1], allowMissing)
	}
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Panic while looking up %q: %s\n", name, r)
		}
	}()
Outer:
	for _, ctx := range contextChain {
		v := ctx.(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 {
						return v.Method(i).Call(nil)[0], nil
					}
				}
			}
			if name == "." {
				return v, nil
			}
			switch av := v; av.Kind() {
			case reflect.Ptr:
				v = av.Elem()
			case reflect.Interface:
				v = av.Elem()
			case reflect.Struct:
				ret := av.FieldByName(name)
				if ret.IsValid() {
					return ret, nil
				}
				continue Outer
			case reflect.Map:
				ret := av.MapIndex(reflect.ValueOf(name))
				if ret.IsValid() {
					return ret, nil
				}
				continue Outer
			default:
				continue Outer
			}
		}
	}
	if allowMissing {
		return reflect.Value{}, nil
	}
	return reflect.Value{}, fmt.Errorf("Missing variable %q", name)
}
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.
package mustache
import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"strconv"
	"strings"
)
var (
	AllowMissingVariables = true
)
type TagType uint
const (
	Invalid TagType = iota
	Variable
	Section
	InvertedSection
	Partial
)
func (t TagType) String() string {
	if int(t) < len(tagNames) {
		return tagNames[t]
	}
	return "type" + strconv.Itoa(int(t))
}
var tagNames = []string{
	Invalid:         "Invalid",
	Variable:        "Variable",
	Section:         "Section",
	InvertedSection: "InvertedSection",
	Partial:         "Partial",
}
type Tag interface {
	Type() TagType
	Name() string
	Tags() []Tag
}
type textElement struct {
	text []byte
}
type varElement struct {
	name string
	raw  bool
}
type sectionElement struct {
	name      string
	inverted  bool
	startline int
	elems     []interface{}
}
type partialElement struct {
	name string
	prov PartialProvider
}
type Template struct {
	data    string
	otag    string
	ctag    string
	p       int
	curline int
	dir     string
	elems   []interface{}
	partial PartialProvider
}
type parseError struct {
	line    int
	message string
}
func (tmpl *Template) Tags() []Tag {
	return extractTags(tmpl.elems)
}
func extractTags(elems []interface{}) []Tag {
	tags := make([]Tag, 0, len(elems))
	for _, elem := range elems {
		switch elem := elem.(type) {
		case *varElement:
			tags = append(tags, elem)
		case *sectionElement:
			tags = append(tags, elem)
		case *partialElement:
			tags = append(tags, elem)
		}
	}
	return tags
}
func (e *varElement) Type() TagType {
	return Variable
}
func (e *varElement) Name() string {
	return e.name
}
func (e *varElement) Tags() []Tag {
	panic("mustache: Tags on Variable type")
}
func (e *sectionElement) Type() TagType {
	if e.inverted {
		return InvertedSection
	}
	return Section
}
func (e *sectionElement) Name() string {
	return e.name
}
func (e *sectionElement) Tags() []Tag {
	return extractTags(e.elems)
}
func (e *partialElement) Type() TagType {
	return Partial
}
func (e *partialElement) Name() string {
	return e.name
}
func (e *partialElement) Tags() []Tag {
	return nil
}
func (p parseError) Error() string {
	return fmt.Sprintf("line %d: %s", p.line, p.message)
}
func (tmpl *Template) readString(s string) (string, error) {
	newlines := 0
	for i := tmpl.p; ; i++ {
		if i+len(s) > len(tmpl.data) {
			return tmpl.data[tmpl.p:], io.EOF
		}
		if tmpl.data[i] == '\n' {
			newlines++
		}
		if tmpl.data[i] != s[0] {
			continue
		}
		match := true
		for j := 1; j < len(s); j++ {
			if s[j] != tmpl.data[i+j] {
				match = false
				break
			}
		}
		if match {
			e := i + len(s)
			text := tmpl.data[tmpl.p:e]
			tmpl.p = e
			tmpl.curline += newlines
			return text, nil
		}
	}
}
func (tmpl *Template) parsePartial(name string) (*partialElement, error) {
	var prov PartialProvider
	if tmpl.partial == nil {
		prov = &FileProvider{
			Paths: []string{tmpl.dir, " "},
		}
	} else {
		prov = tmpl.partial
	}
	return &partialElement{
		name: name,
		prov: prov,
	}, nil
}
func (tmpl *Template) parseSection(section *sectionElement) error {
	for {
		text, err := tmpl.readString(tmpl.otag)
		if err == io.EOF {
			return parseError{section.startline, "Section " + section.name + " has no closing tag"}
		}
		text = text[0 : len(text)-len(tmpl.otag)]
		section.elems = append(section.elems, &textElement{[]byte(text)})
		if tmpl.p < len(tmpl.data) && tmpl.data[tmpl.p] == '{' {
			text, err = tmpl.readString("}" + tmpl.ctag)
		} else {
			text, err = tmpl.readString(tmpl.ctag)
		}
		if err == io.EOF {
			return parseError{tmpl.curline, "unmatched open tag"}
		}
		tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
		if len(tag) == 0 {
			return parseError{tmpl.curline, "empty tag"}
		}
		switch tag[0] {
		case '!':
			break
		case '#', '^':
			name := strings.TrimSpace(tag[1:])
			if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
				tmpl.p++
			} else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
				tmpl.p += 2
			}
			se := sectionElement{name, tag[0] == '^', tmpl.curline, []interface{}{}}
			err := tmpl.parseSection(&se)
			if err != nil {
				return err
			}
			section.elems = append(section.elems, &se)
		case '/':
			name := strings.TrimSpace(tag[1:])
			if name != section.name {
				return parseError{tmpl.curline, "interleaved closing tag: " + name}
			}
			return nil
		case '>':
			name := strings.TrimSpace(tag[1:])
			partial, err := tmpl.parsePartial(name)
			if err != nil {
				return err
			}
			section.elems = append(section.elems, partial)
		case '=':
			if tag[len(tag)-1] != '=' {
				return parseError{tmpl.curline, "Invalid meta tag"}
			}
			tag = strings.TrimSpace(tag[1 : len(tag)-1])
			newtags := strings.SplitN(tag, " ", 2)
			if len(newtags) == 2 {
				tmpl.otag = newtags[0]
				tmpl.ctag = newtags[1]
			}
		case '{':
			if tag[len(tag)-1] == '}' {
				section.elems = append(section.elems, &varElement{tag[1 : len(tag)-1], true})
			}
		default:
			section.elems = append(section.elems, &varElement{tag, false})
		}
	}
}
func (tmpl *Template) parse() error {
	for {
		text, err := tmpl.readString(tmpl.otag)
		if err == io.EOF {
			tmpl.elems = append(tmpl.elems, &textElement{[]byte(text)})
			return nil
		}
		text = text[0 : len(text)-len(tmpl.otag)]
		tmpl.elems = append(tmpl.elems, &textElement{[]byte(text)})
		if tmpl.p < len(tmpl.data) && tmpl.data[tmpl.p] == '{' {
			text, err = tmpl.readString("}" + tmpl.ctag)
		} else {
			text, err = tmpl.readString(tmpl.ctag)
		}
		if err == io.EOF {
			return parseError{tmpl.curline, "unmatched open tag"}
		}
		tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
		if len(tag) == 0 {
			return parseError{tmpl.curline, "empty tag"}
		}
		switch tag[0] {
		case '!':
			break
		case '#', '^':
			name := strings.TrimSpace(tag[1:])
			if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
				tmpl.p++
			} else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
				tmpl.p += 2
			}
			se := sectionElement{name, tag[0] == '^', tmpl.curline, []interface{}{}}
			err := tmpl.parseSection(&se)
			if err != nil {
				return err
			}
			tmpl.elems = append(tmpl.elems, &se)
		case '/':
			return parseError{tmpl.curline, "unmatched close tag"}
		case '>':
			name := strings.TrimSpace(tag[1:])
			partial, err := tmpl.parsePartial(name)
			if err != nil {
				return err
			}
			tmpl.elems = append(tmpl.elems, partial)
		case '=':
			if tag[len(tag)-1] != '=' {
				return parseError{tmpl.curline, "Invalid meta tag"}
			}
			tag = strings.TrimSpace(tag[1 : len(tag)-1])
			newtags := strings.SplitN(tag, " ", 2)
			if len(newtags) == 2 {
				tmpl.otag = newtags[0]
				tmpl.ctag = newtags[1]
			}
		case '{':
			if tag[len(tag)-1] == '}' {
				tmpl.elems = append(tmpl.elems, &varElement{tag[1 : len(tag)-1], true})
			}
		default:
			tmpl.elems = append(tmpl.elems, &varElement{tag, false})
		}
	}
}