新增: u-tpl SQL 模板引擎完整实现
- Lexer/Parser/Executor 三阶段架构
- #{param} 参数化 + ${raw} 原样替换 + 白名单安全策略
- @if/@for/@tpl/@include/@namespace 控制流
- 表达式引擎: 比较、逻辑、nil 检查、len() 内置函数
- 支持 ?/$1/:1 多数据库占位符风格
- 零依赖,纯 Go 标准库实现
This commit is contained in:
73
internal/include.go
Normal file
73
internal/include.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IncludeResolver func(path string) (string, error)
|
||||
|
||||
type IncludeManager struct {
|
||||
resolver IncludeResolver
|
||||
stack []string
|
||||
}
|
||||
|
||||
func NewIncludeManager(resolver IncludeResolver) *IncludeManager {
|
||||
return &IncludeManager{resolver: resolver}
|
||||
}
|
||||
|
||||
func (m *IncludeManager) Resolve(path string) (string, error) {
|
||||
return m.resolveInternal(path, m.stack)
|
||||
}
|
||||
|
||||
func (m *IncludeManager) resolveInternal(path string, stack []string) (string, error) {
|
||||
for i, p := range stack {
|
||||
if p == path {
|
||||
return "", fmt.Errorf("circular include detected: %s is already in the include chain at depth %d", path, i)
|
||||
}
|
||||
}
|
||||
|
||||
src, err := m.resolver(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to resolve include %q: %w", path, err)
|
||||
}
|
||||
|
||||
newStack := make([]string, len(stack)+1)
|
||||
copy(newStack, stack)
|
||||
newStack[len(stack)] = path
|
||||
|
||||
return m.expandIncludes(src, newStack)
|
||||
}
|
||||
|
||||
func (m *IncludeManager) expandIncludes(src string, stack []string) (string, error) {
|
||||
result := src
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
start := strings.Index(result[offset:], `@include("`)
|
||||
if start < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
absStart := offset + start
|
||||
pathStart := absStart + len(`@include("`)
|
||||
|
||||
end := strings.Index(result[pathStart:], `")`)
|
||||
if end < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
includePath := result[pathStart : pathStart+end]
|
||||
absEnd := pathStart + end + len(`")`)
|
||||
|
||||
resolved, err := m.resolveInternal(includePath, stack)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result = result[:absStart] + resolved + result[absEnd:]
|
||||
offset = absStart + len(resolved)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user