新增: @use 同文件片段复用

支持 @use("name") 引用同一文件内 @tpl 定义的块,
消除 _list/_count 模板中 WHERE 条件重复问题。
This commit is contained in:
2026-04-01 01:59:51 +08:00
parent a6847c7d18
commit 1b5b6aff8f
7 changed files with 190 additions and 8 deletions

View File

@@ -15,6 +15,7 @@ type Executor struct {
style PlaceholderStyle
rawPolicy rawValidator
strict bool
blocks map[string][]Node
}
type Result struct {
@@ -22,11 +23,12 @@ type Result struct {
Args []any
}
func NewExecutor(style PlaceholderStyle, rawPolicy rawValidator, strict bool) *Executor {
func NewExecutor(style PlaceholderStyle, rawPolicy rawValidator, strict bool, blocks map[string][]Node) *Executor {
return &Executor{
style: style,
rawPolicy: rawPolicy,
strict: strict,
blocks: blocks,
}
}
@@ -99,6 +101,17 @@ func (e *Executor) walk(ctx *Context, ph *Placeholder, sql *strings.Builder, arg
case *BlockNode, *NamespaceNode, *IncludeNode, *CommentNode:
// skip
case *UseNode:
if e.blocks == nil {
return fmt.Errorf("line %d, col %d: @use(\"%s\") no blocks available", n.Pos.Line, n.Pos.Col, n.Name)
}
blockNodes, ok := e.blocks[n.Name]
if !ok {
return fmt.Errorf("line %d, col %d: @use(\"%s\") block not found", n.Pos.Line, n.Pos.Col, n.Name)
}
if err := e.walk(ctx, ph, sql, args, blockNodes); err != nil {
return err
}
}
}
return nil

View File

@@ -16,6 +16,7 @@ const (
TokTplStart
TokIncludeStart
TokNamespaceStart
TokUseStart
TokElse
TokComment
TokEOF
@@ -173,6 +174,7 @@ func (l *Lexer) tryDirective() (Token, bool) {
{[]rune("@tpl(\""), TokTplStart, 5},
{[]rune("@include(\""), TokIncludeStart, 10},
{[]rune("@namespace(\""), TokNamespaceStart, 12},
{[]rune("@use(\""), TokUseStart, 6},
}
for _, d := range directives {

View File

@@ -85,6 +85,13 @@ type CommentNode struct {
func (n *CommentNode) nodeType() string { return "Comment" }
type UseNode struct {
Pos Pos
Name string
}
func (n *UseNode) nodeType() string { return "Use" }
type ExprType int
const (

View File

@@ -96,6 +96,13 @@ func (p *Parser) Parse() ([]Node, error) {
}
nodes = append(nodes, node)
case TokUseStart:
node, err := p.parseUse(tok)
if err != nil {
return nil, err
}
nodes = append(nodes, node)
case TokElse:
return nil, fmt.Errorf("line %d, col %d: unexpected else", tok.Pos.Line, tok.Pos.Col)
@@ -521,6 +528,26 @@ func (p *Parser) expandInclude(tok Token) ([]Node, error) {
return subParser.Parse()
}
func (p *Parser) parseUse(tok Token) (*UseNode, error) {
runePos := p.runePosFromToken(tok)
name, quotePos, err := p.readUntilQuote(runePos)
if err != nil {
return nil, fmt.Errorf("line %d, col %d: unterminated @use name", tok.Pos.Line, tok.Pos.Col)
}
name = strings.TrimSpace(name)
endPos := quotePos + 1
if endPos < len(p.input) && p.input[endPos] == ')' {
endPos++
}
p.consumeTokensForRuneRange(runePos, endPos)
return &UseNode{
Pos: tok.Pos,
Name: name,
}, nil
}
func (p *Parser) parseNamespace(tok Token, nodeCount int) (*NamespaceNode, error) {
if nodeCount > 0 {
return nil, fmt.Errorf("line %d, col %d: @namespace must be at the top of the file", tok.Pos.Line, tok.Pos.Col)
@@ -695,6 +722,12 @@ func (p *Parser) parseBlockBodyWithElse(blockType string) ([]Node, []Node, []*El
return nil, nil, nil, err
}
body = append(body, subNodes...)
case TokUseStart:
node, err := p.parseUse(tok)
if err != nil {
return nil, nil, nil, err
}
body = append(body, node)
case TokNamespaceStart:
return nil, nil, nil, fmt.Errorf("line %d, col %d: @namespace must be at file top level", tok.Pos.Line, tok.Pos.Col)
}