From 010bcd15bcc2b12683e7e381e4638101ee2048ca Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Tue, 29 Sep 2026 00:15:54 +0200 Subject: [PATCH] Carry the linked arm on renderEdge and reader, and drop the done round item --- drawfence.go | 24 +++++++----------------- graph.go | 13 +++++++++---- holdfence.go | 26 ++++++++++++++------------ todo.md | 2 -- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/drawfence.go b/drawfence.go index eb08c3f..86c5192 100644 --- a/drawfence.go +++ b/drawfence.go @@ -96,8 +96,8 @@ func checkOwnFamily(t *template) error { } seen[n] = true for _, e := range renderEdges(n) { - if a, isRef := refRead(n, e.label); isRef { - if head, isTable := n.(*template).head(a.key).(*table); isTable && head.familyRoot() == own.familyRoot() { + if e.readsRef() { + if head, isTable := n.(*template).head(e.read.key).(*table); isTable && head.familyRoot() == own.familyRoot() { return e, head, true } } @@ -161,7 +161,7 @@ func (c *drawCheck) hasRead(n node, stopAtGroup bool) bool { } r := false for _, e := range renderEdges(n) { - if readsOnEdge(n, e.label) || (walksInto(e.to, stopAtGroup) && c.hasRead(e.to, stopAtGroup)) { + if readsOnEdge(n, e) || (walksInto(e.to, stopAtGroup) && c.hasRead(e.to, stopAtGroup)) { r = true break } @@ -173,9 +173,8 @@ func (c *drawCheck) hasRead(n node, stopAtGroup bool) bool { return r } -func readsOnEdge(n node, label string) bool { - a, isRef := refRead(n, label) - return isRef && (len(a.tail) > 0 || readsTable(n, a)) +func readsOnEdge(n node, e renderEdge) bool { + return e.readsRef() && (len(e.read.tail) > 0 || readsTable(n, e.read)) } func walksInto(to node, stopAtGroup bool) bool { @@ -203,16 +202,6 @@ func readsTable(n node, a arm) bool { return isTable } -// refRead is the reference an edge of n reads; false when the edge reads none. -func refRead(n node, label string) (arm, bool) { - t, isTemplate := n.(*template) - if !isTemplate { - return arm{}, false - } - a := splitArm(label, t.link.refs) - return a, isRef(a.key) -} - // checkColumnDraws fences a record's columns as one render. func checkColumnDraws(t *template, columns []string) error { return columnDraws(t, columns).check() } @@ -342,7 +331,8 @@ func (w *drawWalk) walk(n node, at drawAt) { // edge records the reference an edge reads, then walks on with every row the read // pins entered, so a selected row renders only its own cells. func (w *drawWalk) edge(from node, e renderEdge, at drawAt) { - if a, reads := refRead(from, e.label); reads { + if e.readsRef() { + a := e.read tr := tableReadOf(from.(*template).head(a.key), a, e.to) if k := (readKey{at.group, a.path, w.pinsID(&at.pins), w.pinsID(&at.wholePins)}); !w.read[k] { w.read[k] = true diff --git a/graph.go b/graph.go index d47e196..ef02180 100644 --- a/graph.go +++ b/graph.go @@ -96,15 +96,20 @@ func sortedNames(m map[string]node) []string { } // renderEdge is a child a node renders into, labelled by what reaches it (a field -// name, reference, or choice index) for a readable cycle report. operand names -// the builtin when the label is its operand rather than a token, so an error can -// name it the way the author wrote it. +// name, reference, or choice index) for a readable cycle report. read is the linked +// arm a template's edge reads, zero on any other. operand names the builtin when the +// label is its operand rather than a token, so an error can name it the way the +// author wrote it. type renderEdge struct { to node label string + read arm operand string } +// readsRef reports whether the edge reads a reference. +func (e renderEdge) readsRef() bool { return isRef(e.read.key) } + // reached names an edge as the author spelled it, the vocabulary pathKeyReaders uses // for the sibling fence. func (e renderEdge) reached() string { @@ -133,7 +138,7 @@ func renderEdges(n node) []renderEdge { return } for _, leaf := range pathLeaves(c, a.tail) { - es = append(es, renderEdge{leaf, a.spelling, operand}) + es = append(es, renderEdge{leaf, a.spelling, a, operand}) } } for _, o := range n.compiled.ops { diff --git a/holdfence.go b/holdfence.go index 83a14fb..a91d6aa 100644 --- a/holdfence.go +++ b/holdfence.go @@ -56,8 +56,8 @@ func heldNodes(t *template, name string, readers []reader) map[node]bool { return held } for _, r := range readers { - if a := splitArm(r.spelling, t.link.refs); a.key == name { - coverPath(t.head(name), a.tail, held) + if r.a.key == name { + coverPath(t.head(name), r.a.tail, held) } } return held @@ -74,7 +74,7 @@ func checkNameHeld(t *template, name string, readers []reader) error { reader, isPath := t.compiled.pathKeys[name] seen := map[node]bool{} for _, e := range renderEdges(t) { - if splitArm(e.label, t.link.refs).key == name || !renders(e.to, held, seen) { + if e.read.key == name || !renders(e.to, held, seen) { continue } if isPath { @@ -134,7 +134,7 @@ func operandDraw(n node, into map[node]bool) { } into[n] = true for _, e := range renderEdges(n) { - if isRef(e.label) { + if e.readsRef() { continue } operandDraw(e.to, into) @@ -181,20 +181,22 @@ func checkNoOverlap(ops []op, pathKeys map[string]string) error { names := pathKeyReaders(ops, pathKeys) // Stable over one format-order scan, so two readers of one name (a token and a // calc operand both naming "p") are reported as the format writes them. - sort.SliceStable(names, func(i, j int) bool { return names[i].path < names[j].path }) + sort.SliceStable(names, func(i, j int) bool { return names[i].a.path < names[j].a.path }) for i, level := range names { for _, path := range names[i+1:] { - if strings.HasPrefix(path.path, level.path+".") { - return fmt.Errorf("%s renders a level that {%s} reads a path into; name the fields you want instead", level.label, path.spelling) + if strings.HasPrefix(path.a.path, level.a.path+".") { + return fmt.Errorf("%s renders a level that {%s} reads a path into; name the fields you want instead", level.label, path.a.spelling) } } } return nil } -// reader is one way a format reaches a path key: as written, by its path, -// and how to name it. -type reader struct{ spelling, path, label string } +// reader is one way a format reaches a path key, and how to name it. +type reader struct { + a arm + label string +} // pathKeyReaders lists every way a format reaches a sibling path key, in the order the // format writes them. An operand renders its field, so it names a level exactly @@ -204,12 +206,12 @@ func pathKeyReaders(ops []op, pathKeys map[string]string) []reader { for _, o := range ops { for _, a := range o.operands { if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) { - names = append(names, reader{a.spelling, a.path, fmt.Sprintf("%s operand %q", o.fn, a.spelling)}) + names = append(names, reader{a, fmt.Sprintf("%s operand %q", o.fn, a.spelling)}) } } for _, a := range o.arms { if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) { - names = append(names, reader{a.spelling, a.path, "token {" + a.spelling + "}"}) + names = append(names, reader{a, "token {" + a.spelling + "}"}) } } } diff --git a/todo.md b/todo.md index a295900..d63b1c4 100644 --- a/todo.md +++ b/todo.md @@ -23,8 +23,6 @@ Shape 5.8, Self-sufficiency 5.7), every seat capped by Locality, all nine naming This round, in order: -- Carry the arm on `renderEdge` so `graph.go`, `drawfence.go` and `holdfence.go` stop - re-parsing `label` through `splitArm`, whose key differs before and after link. - Give `pathCover` a doc comment and say in each path walk's comment how it differs from `pathCheck` at a choice and a table: every seat diffed the four loops by eye. - Make `disagreement` a table keyed by the two datatypes and the proof, dropping the