Files
marka/template/compile.go
2025-09-30 19:28:56 +02:00

88 lines
2.1 KiB
Go

package template
import "strings"
// CompileTemplate scans once, emitting:
// - data blocks: inner content between a line that's exactly "{" and a line that's exactly "}"
// - matching blocks: gaps between data blocks (excluding the brace lines themselves)
func CompileTemplate(templateSource string) ([]Block, error) {
templateSource = strings.TrimSuffix(templateSource, "\n")
var out []Block
var curlyIndex int
const OPENING = '{'
const CLOSING = '}'
var start int
var blockType BlockType
template := NewSlice(templateSource)
if template.Len() > 0 && template.At(0) == OPENING {
blockType = DataBlock
} else {
blockType = MatchingBlock
}
for i, r := range template.Chars() {
nextCurlyIndex := curlyIndex
switch r {
case OPENING:
nextCurlyIndex++
case CLOSING:
nextCurlyIndex--
}
if curlyIndex == 0 && nextCurlyIndex == 1 {
if i > start {
block, err := ParseTemplateBlock(template.Slice(start, i), blockType)
if err != nil {
return nil, NewErrorf("cannot parse block @pos -> %w", err).WithPosition(start, i)
}
out = append(out, block)
}
start = i
blockType = DataBlock
} else if curlyIndex == 1 && nextCurlyIndex == 0 {
if i > start {
block, err := ParseTemplateBlock(template.Slice(start, i+1), blockType)
if err != nil {
return nil, NewErrorf("cannot parse block @pos -> %w", err).WithPosition(start, i+1)
}
out = append(out, block)
}
nextChar := ' '
if i+1 < template.Len() {
nextChar = rune(template.At(i + 1))
}
if nextChar == OPENING {
start = i + 1
blockType = DataBlock
} else {
start = i + 1
blockType = MatchingBlock
}
}
curlyIndex = nextCurlyIndex
}
if curlyIndex != 0 {
return nil, NewErrorf("unclosed block").WithPosition(start, template.Len())
}
if start < template.Len() {
block, err := ParseTemplateBlock(template.Slice(start, template.Len()), blockType)
if err != nil {
return nil, NewErrorf("cannot parse final block @pos -> %w", err).WithPosition(start, template.Len())
}
out = append(out, block)
}
return out, nil
}