feat: some experiments
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
// Package blocks contains the logic for parsing template blocks.
|
||||
// Package template contains the logic for parsing templates.
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -16,8 +15,8 @@ const (
|
||||
)
|
||||
|
||||
// DetectTemplateType checks if the template is short or long.
|
||||
func DetectTemplateType(tmpl string) TemplateType {
|
||||
trimmed := strings.TrimSpace(tmpl)
|
||||
func DetectTemplateType(tmpl Slice) TemplateType {
|
||||
trimmed := strings.TrimSpace(tmpl.String())
|
||||
|
||||
// Short type: starts with "{" and ends with "}" on a single line,
|
||||
// and contains "|" or "," inside for inline definition
|
||||
@@ -43,15 +42,15 @@ func DetectTemplateType(tmpl string) TemplateType {
|
||||
return InvalidTemplate
|
||||
}
|
||||
|
||||
func cleanTemplate(input string) string {
|
||||
s := strings.TrimSpace(input)
|
||||
func cleanTemplate(input Slice) string {
|
||||
s := strings.TrimSpace(input.String())
|
||||
s = strings.TrimPrefix(s, "{")
|
||||
s = strings.TrimSuffix(s, "}")
|
||||
s = strings.Trim(s, "\n")
|
||||
return s
|
||||
}
|
||||
|
||||
func ParseTemplateBlock(template string, blockType BlockType) (block Block, err error) {
|
||||
func ParseTemplateBlock(template Slice, blockType BlockType) (block Block, err error) {
|
||||
if blockType == MatchingBlock {
|
||||
return Block{
|
||||
Type: MatchingBlock,
|
||||
@@ -66,5 +65,5 @@ func ParseTemplateBlock(template string, blockType BlockType) (block Block, err
|
||||
return parseYamlTemplate(template)
|
||||
}
|
||||
|
||||
return block, fmt.Errorf("invalid template")
|
||||
return block, NewErrorf("invalid template").WithPosition(template.start, template.end)
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseShortTemplate(input string) (Block, error) {
|
||||
func parseShortTemplate(input Slice) (Block, error) {
|
||||
split := strings.Split(cleanTemplate(input), "|")
|
||||
if len(split) < 1 {
|
||||
return Block{}, fmt.Errorf("invalid short template")
|
||||
|
@@ -25,7 +25,7 @@ type yamlField struct {
|
||||
PathAlias []string `yaml:"pathAlias,omitempty"`
|
||||
}
|
||||
|
||||
func parseYamlTemplate(input string) (block Block, err error) {
|
||||
func parseYamlTemplate(input Slice) (block Block, err error) {
|
||||
var blk yamlBlock
|
||||
|
||||
cleaned := cleanTemplate(input)
|
||||
@@ -34,7 +34,7 @@ func parseYamlTemplate(input string) (block Block, err error) {
|
||||
dec.KnownFields(true)
|
||||
|
||||
if err := dec.Decode(&blk); err != nil {
|
||||
return block, fmt.Errorf("content '%q': %w", cleaned, err)
|
||||
return block, NewErrorf("content '%q' -> %w", cleaned, err).WithPosition(input.start, input.end)
|
||||
}
|
||||
|
||||
if blk.Path == "" {
|
||||
@@ -47,7 +47,7 @@ func parseYamlTemplate(input string) (block Block, err error) {
|
||||
|
||||
codec, err := parseCodecType(blk.Codec)
|
||||
if err != nil {
|
||||
return block, fmt.Errorf("failed to parse codec: %w", err)
|
||||
return block, fmt.Errorf("failed to parse codec -> %w", err)
|
||||
}
|
||||
|
||||
var fields []BlockField
|
||||
@@ -63,7 +63,7 @@ func parseYamlTemplate(input string) (block Block, err error) {
|
||||
|
||||
fieldCodec, err := parseCodecType(field.Codec)
|
||||
if err != nil {
|
||||
return block, fmt.Errorf("failed to parse codec: %w", err)
|
||||
return block, fmt.Errorf("failed to parse codec -> %w", err)
|
||||
}
|
||||
|
||||
fields = append(fields, BlockField{
|
||||
|
@@ -1,13 +1,9 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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(template string) ([]Block, error) {
|
||||
func CompileTemplate(templateSource string) ([]Block, error) {
|
||||
var out []Block
|
||||
var curlyIndex int
|
||||
|
||||
@@ -16,14 +12,15 @@ func CompileTemplate(template string) ([]Block, error) {
|
||||
|
||||
var start int
|
||||
var blockType BlockType
|
||||
template := NewSlice(templateSource)
|
||||
|
||||
if len(template) > 0 && template[0] == OPENING {
|
||||
if template.Len() > 0 && template.At(0) == OPENING {
|
||||
blockType = DataBlock
|
||||
} else {
|
||||
blockType = MatchingBlock
|
||||
}
|
||||
|
||||
for i, r := range template {
|
||||
for i, r := range template.Chars() {
|
||||
|
||||
nextCurlyIndex := curlyIndex
|
||||
|
||||
@@ -36,27 +33,26 @@ func CompileTemplate(template string) ([]Block, error) {
|
||||
|
||||
if curlyIndex == 0 && nextCurlyIndex == 1 {
|
||||
if i > start {
|
||||
block, err := ParseTemplateBlock(template[start:i], blockType)
|
||||
block, err := ParseTemplateBlock(template.Slice(start, i), blockType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse block: %w", err)
|
||||
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[start:i+1], blockType)
|
||||
block, err := ParseTemplateBlock(template.Slice(start, i), blockType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse block: %w", err)
|
||||
return nil, NewErrorf("cannot parse block @pos -> %w", err).WithPosition(start, i)
|
||||
}
|
||||
out = append(out, block)
|
||||
}
|
||||
|
||||
nextChar := ' '
|
||||
if i+1 < len(template) {
|
||||
nextChar = rune(template[i+1])
|
||||
if i+1 < template.Len() {
|
||||
nextChar = rune(template.At(i + 1))
|
||||
}
|
||||
|
||||
if nextChar == OPENING {
|
||||
|
36
template/error.go
Normal file
36
template/error.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
err error
|
||||
start, end int
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
content := e.err.Error()
|
||||
|
||||
if strings.Contains(content, " @pos ") {
|
||||
if e.start == e.end {
|
||||
return strings.ReplaceAll(content, " @pos ", " ")
|
||||
}
|
||||
return strings.ReplaceAll(content, " @pos ", fmt.Sprintf(" position=%d:%d ", e.start, e.end))
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
func NewErrorf(msg string, args ...any) Error {
|
||||
return Error{
|
||||
err: fmt.Errorf(msg, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (e Error) WithPosition(start, end int) Error {
|
||||
e.start = start
|
||||
e.end = end
|
||||
return e
|
||||
}
|
46
template/slice.go
Normal file
46
template/slice.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package template
|
||||
|
||||
func NewSlice(s string) Slice {
|
||||
return Slice{
|
||||
source: &s,
|
||||
start: 0,
|
||||
end: len(s),
|
||||
}
|
||||
}
|
||||
|
||||
type Slice struct {
|
||||
source *string
|
||||
start, end int
|
||||
}
|
||||
|
||||
func (s Slice) Chars() []byte {
|
||||
return []byte((*s.source)[s.start:s.end])
|
||||
}
|
||||
|
||||
func (s Slice) Len() int {
|
||||
return s.end - s.start
|
||||
}
|
||||
|
||||
func (s Slice) At(i int) byte {
|
||||
return (*s.source)[s.start+i]
|
||||
}
|
||||
|
||||
func SliceFromString(s string, start, end int) Slice {
|
||||
return Slice{
|
||||
source: &s,
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Slice) String() string {
|
||||
return (*s.source)[s.start:s.end]
|
||||
}
|
||||
|
||||
func (s Slice) Slice(start, end int) Slice {
|
||||
return Slice{
|
||||
source: s.source,
|
||||
start: s.start + start,
|
||||
end: s.start + end,
|
||||
}
|
||||
}
|
@@ -22,9 +22,9 @@ type Block struct {
|
||||
Fields []BlockField
|
||||
Optional bool
|
||||
Value any
|
||||
content string
|
||||
content Slice
|
||||
}
|
||||
|
||||
func (b Block) GetContent() string {
|
||||
return b.content
|
||||
return b.content.String()
|
||||
}
|
||||
|
Reference in New Issue
Block a user