Return link's and format compile's outputs as templateLink and formatOps, assigned once in linkTemplate

This commit is contained in:
2026-09-28 23:37:32 +02:00
committed by Lillem4n
parent 243094e4ab
commit e257dc0e6e
17 changed files with 120 additions and 118 deletions
+36 -25
View File
@@ -74,52 +74,63 @@ func refSegments(name string, folder []string) ([]string, error) {
func linkRefs(root map[string]node) error {
return eachTemplate(root, func(folder []string, path string, t *template) error {
category := strings.Join(strings.Split(path, ".")[:len(folder)+1], ".")
t.keyDrawGroup(category)
return linkTemplateRefs(folder, path, category, t, root)
return linkTemplate(folder, path, category, t, root)
})
}
func linkTemplateRefs(folder []string, path, category string, t *template, root map[string]node) error {
if t.cellOf != nil {
path = fmt.Sprintf("%s, line %d", path, t.cellRow+2)
// linkTemplate resolves t's references in category, "" for an inline template, and
// compiles its format against them.
func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error {
if t.cell.table != nil {
path = fmt.Sprintf("%s, line %d", path, t.cell.row+2)
}
if names := refTokens(t.tokens); len(names) > 0 {
if err := t.bindRefs(folder, path, category, names, root); err != nil {
return err
}
link, err := t.resolveLink(folder, path, category, root)
if err != nil {
return err
}
if err := t.compileFormat(); err != nil {
compiled, err := compileFormat(t.tokens, link.refs)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
t.readsColumn = columnReadOf(t)
t.link, t.compiled = link, compiled
return nil
}
// bindRefs fills refs and refHeads, refusing a reference to t's own category:
// resolveLink binds every reference t reads, refusing one to t's own category, and keys
// t's draw group by that category, so a name is local to it:
// docs/decisions.md#a-category-never-references-itself-and-a-records-fences-run-at-load
func (t *template) bindRefs(folder []string, path, category string, names []string, root map[string]node) error {
t.refs = make(map[string]refBinding, len(names))
t.refHeads = make(map[string]node, len(names))
func (t *template) resolveLink(folder []string, path, category string, root map[string]node) (templateLink, error) {
var link templateLink
if t.drawGroup != "" {
link.drawGroupKey = category + "/" + t.drawGroup
}
names := refTokens(t.tokens)
if len(names) == 0 {
return link, nil
}
link.refs = make(map[string]refBinding, len(names))
link.refHeads = make(map[string]node, len(names))
for _, name := range names {
segments, err := refSegments(name, folder)
if err != nil {
return fmt.Errorf("%s: reference {%s}: %w", path, name, err)
return link, fmt.Errorf("%s: reference {%s}: %w", path, name, err)
}
categorySegs, target, tail, err := resolveCategory(root, segments)
if err != nil {
return fmt.Errorf("%s: reference {%s}: %w", path, name, err)
return link, fmt.Errorf("%s: reference {%s}: %w", path, name, err)
}
key := "/" + strings.Join(categorySegs, ".")
if category != "" && key == "/"+category {
return fmt.Errorf("%s: reference {%s}: names the category it sits in; read a sibling field as a path, or move the shared value into its own category and reference that", path, name)
return link, fmt.Errorf("%s: reference {%s}: names the category it sits in; read a sibling field as a path, or move the shared value into its own category and reference that", path, name)
}
if err := checkPath(target, tail, key); err != nil {
return fmt.Errorf("%s: reference {%s}: %w", path, name, err)
return link, fmt.Errorf("%s: reference {%s}: %w", path, name, err)
}
t.refHeads[key] = target
t.refs[name] = refBinding{key, tail}
link.refHeads[key] = target
link.refs[name] = refBinding{key, tail}
}
return nil
link.readsColumn = columnReadOf(t, link)
return link, nil
}
// columnRead is a record's column read by a format of that one reference alone, which is the
@@ -129,13 +140,13 @@ type columnRead struct {
column node
}
func columnReadOf(t *template) *columnRead {
func columnReadOf(t *template, link templateLink) *columnRead {
name, lone := loneRef(t.tokens)
if !lone || t.repeat != 1 {
return nil
}
a := splitArm(name, t.refs)
target, isTemplate := t.head(a.key).(*template)
a := splitArm(name, link.refs)
target, isTemplate := link.refHeads[a.key].(*template)
if !isTemplate || !target.isRecord || len(a.tail) != 1 {
return nil
}