Name the name fences refuseSingleRead and refuseTwiceDrawn, each doc stating the rule it holds

This commit is contained in:
2026-10-07 01:07:57 +02:00
committed by Lillem4n
parent 94e3eabbcf
commit ac44a2f89b
+21 -17
View File
@@ -9,16 +9,15 @@ import (
"github.com/larvit/fejkdata/internal/invariant" "github.com/larvit/fejkdata/internal/invariant"
) )
// checkNameReads refuses each binding of t that checkUses refuses, a read of a name inside the // checkNameReads refuses each binding of t read only once, a read of a name inside the field bound
// field bound to it, and a read {n} beside {n.w} where n's pick renders w twice: the pick keeps one // to it, and a read {n} beside {n.w} where n's pick renders w twice.
// draw of w.
func checkNameReads(label string, t *template, names resolvedNames) error { func checkNameReads(label string, t *template, names resolvedNames) error {
for _, tok := range t.tokens { for _, tok := range t.tokens {
if tok.Kind != grammar.NameBind { if tok.Kind != grammar.NameBind {
continue continue
} }
b := t.nameScope.bindings[tok.Bound] b := t.nameScope.bindings[tok.Bound]
if err := b.checkUses(names.uses[b]); err != nil { if err := b.refuseSingleRead(names.uses[b]); err != nil {
return fmt.Errorf("%s: token {%s}: %w", label, tok.Body, err) return fmt.Errorf("%s: token {%s}: %w", label, tok.Body, err)
} }
} }
@@ -28,7 +27,7 @@ func checkNameReads(label string, t *template, names resolvedNames) error {
return fmt.Errorf("%s: token {%s}: name %q is read inside %q, the field bound to it; read the name outside that field", label, r.o.Body, b.name, b.ref) return fmt.Errorf("%s: token {%s}: name %q is read inside %q, the field bound to it; read the name outside that field", label, r.o.Body, b.name, b.ref)
} }
for _, leaf := range r.a.leaves { for _, leaf := range r.a.leaves {
if err := checkOnce(names.addressed[b], r.a.spelling, leaf, r.a.key()); err != nil { if err := refuseTwiceDrawn(names.addressed[b], r.a.spelling, leaf, r.a.key()); err != nil {
return fmt.Errorf("%s: token {%s}: %w", label, r.o.Body, err) return fmt.Errorf("%s: token {%s}: %w", label, r.o.Body, err)
} }
} }
@@ -63,11 +62,12 @@ func rendersInside(nodes []node, t *template) bool {
return false return false
} }
// checkUses refuses a binding whose uses read it once at a spot the bound spelling can stand: // refuseSingleRead refuses b where it is read once, outside a repeat nested where it is bound, at a
// that spelling draws the same way without the name. A bound field can stand in only where the // spot the bound spelling can stand: that spelling draws the same without the name, {/word} for
// reading template reaches the binder through fields. Where the spelling would be a CLI argument // {/word as w}{w}. A bound field stands in only where the reading template reaches the binder
// or tag of one reference alone, that entry point's own refusal then names the bare path. // through fields, and a calc operand reads neither a reference nor a path. Where the spelling would
func (b *nameBinding) checkUses(uses []nameUse) error { // be a CLI argument or tag of one reference alone, that entry point's own refusal names the bare path.
func (b *nameBinding) refuseSingleRead(uses []nameUse) error {
if len(uses) == 0 { if len(uses) == 0 {
panic(invariant.Broken("name %q has no read at resolve, though its compile found one", b.name)) panic(invariant.Broken("name %q has no read at resolve, though its compile found one", b.name))
} }
@@ -120,23 +120,27 @@ func calcReads(spelling string) bool {
return err == nil && isVar && v.Name == spelling return err == nil && isVar && v.Name == spelling
} }
// checkOnce walks n, rendering at key under a pick, into each level a read of the name addresses: // refuseTwiceDrawn refuses read, a read of a name rendering n at key under its pick, where n renders
// the keys of addressed. // a field twice that another read of the name addresses: the pick keeps one draw of the field, so
func checkOnce(addressed map[pickKey]string, read string, n node, key pickKey) error { // {n.w} beside {n} for a category {w}-{w} cannot say which it reads. addressed maps each level a
// read of the name addresses to that read. It walks into every addressed level n renders.
func refuseTwiceDrawn(addressed map[pickKey]string, read string, n node, key pickKey) error {
switch n := n.(type) { switch n := n.(type) {
case *choice: case *choice:
for _, item := range n.items { for _, item := range n.items {
if err := checkOnce(addressed, read, item, key); err != nil { if err := refuseTwiceDrawn(addressed, read, item, key); err != nil {
return err return err
} }
} }
case *template: case *template:
return checkTemplateOnce(addressed, read, n, key) return refuseTwiceDrawnIn(addressed, read, n, key)
} }
return nil return nil
} }
func checkTemplateOnce(addressed map[pickKey]string, read string, t *template, key pickKey) error { // refuseTwiceDrawnIn is refuseTwiceDrawn over t's own reads of its fields; a reference or a read
// through a name draws apart from the pick.
func refuseTwiceDrawnIn(addressed map[pickKey]string, read string, t *template, key pickKey) error {
reads := map[string]int{} reads := map[string]int{}
var into []arm var into []arm
for _, o := range t.compiled.ops { for _, o := range t.compiled.ops {
@@ -157,7 +161,7 @@ func checkTemplateOnce(addressed map[pickKey]string, read string, t *template, k
} }
for _, a := range into { for _, a := range into {
for _, leaf := range a.leaves { for _, leaf := range a.leaves {
if err := checkOnce(addressed, read, leaf, key.under(a.key())); err != nil { if err := refuseTwiceDrawn(addressed, read, leaf, key.under(a.key())); err != nil {
return err return err
} }
} }