Name a calc operand as one when the fence reports it
A renderEdge label can be an operand name, and both messages printed it as
{name} — so an author was told a token renders the level when no such token
existed in the format. An edge now carries whether its label is an operand and
names itself the way boundReaders does, so both fences speak one language.
The narrowed operand cover fixed the other half: the render closure pulled the
other operand into the cover, which could report the two sides the wrong way
round ({n} renders "m" where it is m that reaches n).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+35
-23
@@ -65,7 +65,7 @@ func checkBoundLevelsHeld(root map[string]node) error {
|
|||||||
if isPath {
|
if isPath {
|
||||||
cover(t.fields[head], held)
|
cover(t.fields[head], held)
|
||||||
} else {
|
} else {
|
||||||
coverRendered(t.fields[head], held)
|
operandDraw(t.fields[head], held)
|
||||||
}
|
}
|
||||||
if len(held) == 0 {
|
if len(held) == 0 {
|
||||||
continue // a fixed string, which cannot disagree with itself
|
continue // a fixed string, which cannot disagree with itself
|
||||||
@@ -79,9 +79,9 @@ func checkBoundLevelsHeld(root map[string]node) error {
|
|||||||
}
|
}
|
||||||
if renders(e.to, held, seen) {
|
if renders(e.to, held, seen) {
|
||||||
if isPath {
|
if isPath {
|
||||||
return fmt.Errorf("%s: {%s} renders %q, which {%s} reads a path into; name the fields you want instead", path, e.label, head, reader)
|
return fmt.Errorf("%s: %s renders %q, which {%s} reads a path into; name the fields you want instead", path, e.reached(), head, reader)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("%s: {%s} renders %q, which a {calc()} also reads; reach it one way so it is drawn once", path, e.label, head)
|
return fmt.Errorf("%s: %s renders %q, which a {calc()} also reads; reach it one way so it is drawn once", path, e.reached(), head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,24 +103,18 @@ func cover(n node, into map[node]bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// coverRendered collects what one held draw of a calc operand answers for: what
|
// operandDraw collects what one held draw of a {calc()} operand answers for: that
|
||||||
// rendering it reaches, following the same edges expand does. An operand is only
|
// one node. A calc renders its operand whole, so the draw is the value that render
|
||||||
// ever rendered — checkNoOverlap rejects a path into one — so its draw fixes that
|
// produces, and another route conflicts only by naming the same node. Containment
|
||||||
// value and nothing else. That is narrower than containment where a sibling the
|
// is the wrong set here — a sibling the operand never renders is no part of its
|
||||||
// operand's format never renders cannot disagree with it, and wider where the
|
// value — and so is the render closure, which would read two names drawing from one
|
||||||
// operand renders through a reference, which is part of the value it produces.
|
// shared source as one draw. A literal is left out for the reason cover leaves one
|
||||||
// Literals are left out for the reason cover leaves them out.
|
// out.
|
||||||
func coverRendered(n node, into map[node]bool) {
|
func operandDraw(n node, into map[node]bool) {
|
||||||
if _, fixed := n.(literal); fixed {
|
if _, fixed := n.(literal); fixed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if into[n] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
into[n] = true
|
into[n] = true
|
||||||
for _, e := range renderEdges(n) {
|
|
||||||
coverRendered(e.to, into)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// renders reports whether rendering n can reach anything in want, following the
|
// renders reports whether rendering n can reach anything in want, following the
|
||||||
@@ -267,11 +261,23 @@ func refTokens(format string) []string {
|
|||||||
return refs
|
return refs
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderEdge is a child a node renders into, labelled by the token that reaches it
|
// renderEdge is a child a node renders into, labelled by what reaches it (a field
|
||||||
// (a field name, reference, or choice index) for a readable cycle report.
|
// name, reference, or choice index) for a readable cycle report. operand marks a
|
||||||
|
// label that is a {calc()} operand name rather than a token, so an error can name
|
||||||
|
// it the way the author wrote it.
|
||||||
type renderEdge struct {
|
type renderEdge struct {
|
||||||
to node
|
to node
|
||||||
label string
|
label string
|
||||||
|
operand bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// reached names an edge as the author spelled it, the vocabulary boundReaders uses
|
||||||
|
// for the sibling fence.
|
||||||
|
func (e renderEdge) reached() string {
|
||||||
|
if e.operand {
|
||||||
|
return fmt.Sprintf("calc operand %q", e.label)
|
||||||
|
}
|
||||||
|
return "{" + e.label + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderEdges lists the children rendering n recurses into, mirroring expand: a
|
// renderEdges lists the children rendering n recurses into, mirroring expand: a
|
||||||
@@ -282,21 +288,27 @@ func renderEdges(n node) []renderEdge {
|
|||||||
case *choice:
|
case *choice:
|
||||||
es := make([]renderEdge, len(n.items))
|
es := make([]renderEdge, len(n.items))
|
||||||
for i, it := range n.items {
|
for i, it := range n.items {
|
||||||
es[i] = renderEdge{it, fmt.Sprintf("[%d]", i)}
|
es[i] = renderEdge{to: it, label: fmt.Sprintf("[%d]", i)}
|
||||||
}
|
}
|
||||||
return es
|
return es
|
||||||
case *template:
|
case *template:
|
||||||
var es []renderEdge
|
var es []renderEdge
|
||||||
for _, name := range append(fieldTokens(n.format), calcOperands(n.format)...) {
|
add := func(name string, operand bool) {
|
||||||
a := splitArm(name)
|
a := splitArm(name)
|
||||||
c, ok := n.fields[a.key]
|
c, ok := n.fields[a.key]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
for _, leaf := range pathLeaves(c, a.tail) {
|
for _, leaf := range pathLeaves(c, a.tail) {
|
||||||
es = append(es, renderEdge{leaf, name})
|
es = append(es, renderEdge{leaf, name, operand})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, name := range fieldTokens(n.format) {
|
||||||
|
add(name, false)
|
||||||
|
}
|
||||||
|
for _, name := range calcOperands(n.format) {
|
||||||
|
add(name, true)
|
||||||
|
}
|
||||||
return es
|
return es
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user