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
+29 -1
View File
@@ -9,6 +9,34 @@ type namedPick struct {
pins pinSet
}
// drawMemo is what a named pick has drawn: the variant each level was drawn as, so every path
// under it reads one variant; the value each read produced, by its path, so the same read written
// twice reads one value; the frames a read opens for the name scopes around where it lands
// (enter); and the rows drawn by each step down after a "..", by level.
type drawMemo struct {
variant map[string]node
value map[string]readValue
enteredFrames map[*nameScope]*pickFrame
steppedDown map[string]*pinSet
}
// stepDownPins is the pins a path steps down into after stepping up to t: kept in m where there
// is one, so every path stepping down there reads one draw.
func (m *drawMemo) stepDownPins(pins *pinSet, t *table, levels []string, at int) *pinSet {
if m == nil {
return pins.above(t)
}
p, ok := m.steppedDown[levels[at]]
if !ok {
p = pins.above(t)
if m.steppedDown == nil {
m.steppedDown = map[string]*pinSet{}
}
m.steppedDown[levels[at]] = p
}
return p
}
// pickFrame is one render of a name scope: a pick per binding, each drawn on its first read.
type pickFrame struct {
scope *nameScope
@@ -117,7 +145,7 @@ func readName(s *generatorState, sc renderScope, a arm) readValue {
return r
}
leaf, pins := p.draw(s, a.named.head, a.steps, a.levels, a.path)
if a.named.field {
if a.named.bindsField() {
return p.renderAt(s, leaf, pins, a.path, sc)
}
sc, mark := sc.enter(leaf, &p.memo)