func (t *Template) Render(vars Vars) (string, error) {
	if len(t.Nodes) == 0 || t.Nodes[0].Blank() {
		return "", nil
	}
	return t.Nodes[0].Render(vars)
}
Render the template with the supplied variables
package liquid
import (
	"errors"
	"fmt"
	"regexp"
	"strings"
)
var (
	fullTokenRegexp         = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v\s*(\w+)\s*(.*)?%v\z`, tagStartRegexp, tagEndRegexp))  
	contentOfVariableRegexp = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v(.*)%v\z`, variableStartRegexp, variableEndRegexp))    
	tokenIsBlankRegexp      = regexp.MustCompile(`\A\s*\z`)
)
const (
	tagStartToken = "{%"
	varStartToken = "{{"
)
type Template struct {
	Nodes []Node
}
type Node interface {
	Render(Vars) (string, error)
	Blank() bool
}
type stringNode string
func (n stringNode) Render(v Vars) (string, error) {
	return string(n), nil
}
func (n stringNode) Blank() bool {
	return n == ""
}
type Tag interface {
	Parse(name, markup string, tokenizer *Tokenizer, ctx *ParseContext) (Node, error)
}
type commentTag struct{}
func (t *commentTag) Parse(name, markup string, tokenizer *Tokenizer, ctx *ParseContext) (Node, error) {
	subctx := &ParseContext{
		line: ctx.line,
		end:  fmt.Sprintf("end%v", name),
	}
	nodelist, err := tokensToNodeList(tokenizer, subctx)
	if err != nil {
		return nil, err
	}
	ctx.line = subctx.line
	return BlockNode{
		Tag:    name,
		markup: markup,
		Nodes:  nodelist,
	}, nil
}
type elseTag struct {
	Params bool
}
func (t *elseTag) Parse(name, markup string, tokenizer *Tokenizer, ctx *ParseContext) (Node, error) {
	if !t.Params {
		if strings.Replace(markup, " ", "", -1) != "{%else%}" {
			return nil, errors.New("else doesn't accept params")
		}
	}
	return elseNode{tag: name, markup: markup}, nil
}
type ifTag struct{}
func (t *ifTag) Parse(name, markup string, tokenizer *Tokenizer, ctx *ParseContext) (Node, error) {
	subctx := &ParseContext{
		line: ctx.line,
		end:  fmt.Sprintf("end%v", name),
		temporaryTags: map[string]Tag{
			"elsif": &elseTag{Params: true},
			"else":  &elseTag{},
		},
	}
	nodelist, err := tokensToNodeList(tokenizer, subctx)
	if err != nil {
		return nil, err
	}
	ctx.line = subctx.line
	return BlockNode{
		Tag:    name,
		Nodes:  nodelist,
		markup: markup,
	}, nil
}
func RegisterTag(name string, tag Tag) {
	RegisteredTags[name] = tag
}
var RegisteredTags = map[string]Tag{
	"comment": &commentTag{},
	"if":      &ifTag{},
}
type ParseContext struct {
	line          int
	end           string
	temporaryTags map[string]Tag
}
func (c *ParseContext) String() string {
	return fmt.Sprintf("Line: %v, End: %v", c.line, c.end)
}
func tokensToNodeList(tokenizer *Tokenizer, ctx *ParseContext) ([]Node, error) {
	var nodeList []Node
	blank := true
	var token string
	var done error
	for done == nil {
		token, done = tokenizer.Next()
		if token == "" {
			continue
		}
		switch {
		case strings.HasPrefix(token, tagStartToken):
			if matched := fullTokenRegexp.FindStringSubmatch(token); len(matched) > 0 {
				markup, tagName := matched[0], matched[1]
				if strings.HasPrefix(tagName, "end") {
					var err error
					if tagName != ctx.end {
						err = LiquidError(fmt.Sprintf("Unexpected end tag: %v, %v", tagName, markup), ctx)
					}
					return nodeList, err
				} else if tag, ok := RegisteredTags[tagName]; ok {
					newTag, err := tag.Parse(tagName, markup, tokenizer, ctx)
					if err != nil {
						return nil, err
					}
					blank = blank && newTag.Blank()
					nodeList = append(nodeList, newTag)
				} else if tag, ok := ctx.temporaryTags[tagName]; ok {
					newTag, err := tag.Parse(tagName, markup, tokenizer, ctx)
					if err != nil {
						return nil, err
					}
					blank = blank && newTag.Blank()
					nodeList = append(nodeList, newTag)
				} else if tagName == "else" || tagName == "end" {
					return nil, ErrSyntax("Unexpected outer 'else' tag")
				} else {
					return nil, ErrSyntax(fmt.Sprintf("Unknown tag '%v'", tagName))
				}
			} else {
				return nil, LiquidError(fmt.Sprintf("Missing tag terminator: %v", token), ctx)
			}
		case strings.HasPrefix(token, varStartToken):
			myVar, err := createVariable(token, ctx)
			if err != nil {
				return nil, err
			}
			nodeList = append(nodeList, myVar)
			blank = false
		default:
			nodeList = append(nodeList, stringNode(token))
			blank = blank && tokenIsBlankRegexp.MatchString(token)
		}
		ctx.line += strings.Count(token, "\n")
	}
	return nodeList, nil
}
func ParseTemplate(template string) (*Template, error) {
	tokenizer := NewTokenizer(template)
	ctx := &ParseContext{line: 0}
	nodeList, err := tokensToNodeList(tokenizer, ctx)
	return &Template{nodeList}, err
}