Answer architecture round 1: refuse a name read inside the field it binds, check every scope's cycles, build a column read in one pass, drop the implicit struct pick for names across tags, and state the fresh-draw rule once

This commit is contained in:
2026-10-05 09:39:18 +02:00
committed by Lillem4n
parent a45edf8fb5
commit 1a15a139c8
23 changed files with 257 additions and 230 deletions
+17 -1
View File
@@ -1,6 +1,7 @@
package fejkdata
import (
"errors"
"fmt"
"slices"
"strings"
@@ -18,6 +19,9 @@ func checkNameReads(path string, t *template) error {
}
}
return namedReads(t, func(o *op, a *arm) error {
if a.named.bindsField() && rendersInside(a.named.head, t) {
return fmt.Errorf("%s: token {%s}: name %q is read inside %q, the field it binds, which would render itself; read the name outside that field", t.site.label(path), o.body, a.named.name, a.named.ref)
}
for _, leaf := range a.leaves {
if err := a.named.checkOnce(a.spelling, leaf, a.path); err != nil {
return fmt.Errorf("%s: token {%s}: %w", t.site.label(path), o.body, err)
@@ -27,11 +31,23 @@ func checkNameReads(path string, t *template) error {
})
}
// rendersInside reports whether t sits in what n contains.
func rendersInside(n node, t *template) bool {
return eachNode(n, "", func(_ string, m node) error {
if m == t {
return errFound
}
return nil
}) != nil
}
var errFound = errors.New("found")
// checkUses refuses a binding read once at a spot the bound spelling can stand: that spelling
// draws the same way without the name. A field stands only beside its binding.
func (b *nameBinding) checkUses() error {
r := b.uses[0]
if len(b.uses) > 1 || r.nested || b.field && r.in != b.binder {
if len(b.uses) > 1 || r.nested || b.bindsField() && r.in != b.binder {
return nil
}
spelling := b.ref