Files
u-tpl/engine.go

131 lines
2.7 KiB
Go

package utpl
import (
"errors"
"fmt"
"gitea.1216.top/lxy/u-tpl/internal"
)
type PlaceholderStyle = internal.PlaceholderStyle
const (
QuestionMark PlaceholderStyle = internal.QuestionMark
DollarNumber = internal.DollarNumber
ColonNumber = internal.ColonNumber
)
type IncludeResolver func(path string) (string, error)
type Option func(*Engine)
type Engine struct {
style internal.PlaceholderStyle
rawPolicy RawPolicy
includeResolver IncludeResolver
strict bool
}
func New(opts ...Option) *Engine {
e := &Engine{
style: internal.QuestionMark,
strict: true,
}
for _, opt := range opts {
opt(e)
}
return e
}
func WithPlaceholderStyle(style PlaceholderStyle) Option {
return func(e *Engine) { e.style = style }
}
func WithRawPolicy(policy RawPolicy) Option {
return func(e *Engine) { e.rawPolicy = policy }
}
func WithIncludeResolver(resolver IncludeResolver) Option {
return func(e *Engine) { e.includeResolver = resolver }
}
func WithStrictMode(strict bool) Option {
return func(e *Engine) { e.strict = strict }
}
func (e *Engine) Parse(name string, source string) (*Template, error) {
lexer := internal.NewLexer(source)
tokens, err := lexer.Tokenize()
if err != nil {
return nil, wrapParseError(err, name)
}
var includeMgr *internal.IncludeManager
if e.includeResolver != nil {
includeMgr = internal.NewIncludeManager(internal.IncludeResolver(e.includeResolver))
}
parser := internal.NewParser(source, tokens, includeMgr)
nodes, err := parser.Parse()
if err != nil {
return nil, wrapParseError(err, name)
}
namespace := ""
blocks := make(map[string][]internal.Node)
var bodyNodes []internal.Node
hasBlocks := false
for _, n := range nodes {
if ns, ok := n.(*internal.NamespaceNode); ok {
namespace = ns.Name
continue
}
if blk, ok := n.(*internal.BlockNode); ok {
hasBlocks = true
fullName := blk.Name
if namespace != "" {
fullName = namespace + "." + blk.Name
}
blocks[fullName] = blk.Body
continue
}
bodyNodes = append(bodyNodes, n)
}
return &Template{
name: name,
engine: e,
nodes: bodyNodes,
blocks: blocks,
hasBlocks: hasBlocks,
namespace: namespace,
}, nil
}
func (e *Engine) MustParse(name string, source string) *Template {
tpl, err := e.Parse(name, source)
if err != nil {
panic(err)
}
return tpl
}
func wrapParseError(err error, name string) error {
if _, ok := err.(*ParseError); ok {
return err
}
msg := fmt.Sprintf("template %q: %s", name, err.Error())
var posErr *internal.PosError
if errors.As(err, &posErr) {
return &ParseError{
Pos: Position{Line: posErr.Line, Column: posErr.Col},
Message: msg,
}
}
return &ParseError{
Pos: Position{},
Message: msg,
}
}