Make the names prose true of the code and readable on one pass, and state each refusal once

This commit is contained in:
2026-10-03 12:23:21 +02:00
committed by Lillem4n
parent 32124c5b1e
commit 62214370ed
10 changed files with 55 additions and 71 deletions
+11 -19
View File
@@ -7,9 +7,9 @@ import (
"strings"
)
// nameScope is where a name is bound: a category, or one iteration of a repeat inside it.
// Every token of the category outside a nested repeat reads the category's names, so one
// field may bind a name and another read it.
// nameScope is where a name is bound: a category, or one iteration of a repeat inside it. A
// token sees its scope's names and every enclosing scope's, so one field may bind a name and
// another read it.
type nameScope struct {
up *nameScope
owner node // the category's root, or the repeat
@@ -17,9 +17,6 @@ type nameScope struct {
order []*nameBinding
}
// nameBinding is one {ref as name} token: the scope it binds name in, its index among
// that scope's bindings, its uses, and, once linked, the head node and tail its
// reference reads and the keys its reads address.
type nameBinding struct {
name string
ref string
@@ -56,7 +53,7 @@ func (sc *nameScope) lookup(name string) *nameBinding {
// bindNames gives every template of a compiled category or inline template the scope its
// names live in, and refuses a name bound twice along one chain of scopes, a binding in a
// choice's item, a name a field spells too, a read no field or name answers, and a binding
// read by nothing, or once whole, which its reference spells.
// read by nothing.
func bindNames(root node) error {
top := &nameScope{owner: root}
scopes := []*nameScope{top}
@@ -100,8 +97,7 @@ func bindNames(root node) error {
return nil
}
// bindAll binds every name t's tokens bind. A choice's item binds none: a read outside the item
// would find the name bound by an item that may never be drawn.
// bindAll binds every name t's tokens bind.
func (sc *nameScope) bindAll(t *template, inChoice bool, where string) error {
for _, tok := range t.tokens {
if tok.kind != nameBind {
@@ -221,7 +217,6 @@ func unresolved(where string, u unboundRead, seen *nameScope, scopes []*nameScop
return fmt.Errorf("%s%w", where, u.err)
}
// isName reports whether a head t reads is a name: neither a reference nor a field.
func (t *template) isName(head string) bool {
return !isRef(head) && t.fields[head] == nil && t.nameScope.lookup(head) != nil
}
@@ -315,10 +310,8 @@ func checkNameReads(path string, t *template) error {
})
}
// checkUses refuses a binding of a category read once whole, which the bare reference spells, and
// reads of b in two draw groups, or inside a repeat, where what b names reads a
// reference path: that path is held per draw group and per iteration, so what the pick kept and
// what it renders afresh would come from two draws of it. todo.md item 4 ends the hold.
// checkUses refuses a binding of a category read once whole, and reads of b in two draw groups,
// or inside a repeat, where what b names reads a reference path.
func (b *nameBinding) checkUses() error {
if r := b.uses[0]; len(b.uses) == 1 && r.tail == "" && !r.nested && len(b.tail) == 0 {
spelling := b.ref
@@ -481,9 +474,9 @@ func (d *renderDraws) depth() int {
return len(d.frameStack.frames)
}
// enter is sc as a read entering a category sees it, landing on n: none of the frames rendering,
// and, from memo, the frame of each scope binding names around n, so every read entering with
// memo reads one pick of each name. It returns the mark that closes those frames.
// enter hides the frames already rendering from a read landing on n, and opens from memo a frame
// for each name scope around n, so every read sharing memo reads one pick of each name. It
// returns the mark that closes those frames.
func (sc renderScope) enter(n node, memo *drawMemo) (renderScope, int) {
sc.base, sc.pick = sc.draws.depth(), nil
for scope := scopeAround(n); scope != nil; scope = scope.up {
@@ -566,8 +559,7 @@ func (sc renderScope) drawRowOf(s *generatorState, t *table) int {
return t.drawRow(s)
}
// keeps reports whether the pick sc renders part of keeps a, a read of t: whether a read of the
// name addresses the level a starts at.
// keeps reports whether a read of sc's name addresses the level a starts at.
func (sc renderScope) keeps(a arm) bool {
_, kept := sc.pick.named.addressed[underKey(sc.pickKey, a.head)]
return kept