Hold what a calc operand's render settled, up to a reference
The fence pinned only the operand node, so a reference reaching into it drew
that value again: {net.v} was a load error while {..cat.net.v} loaded and
disagreed in 20 of 40 renders. The hold now covers what rendering the operand
settles inside itself, so both spellings of that shape are rejected.
The walk stops at a {..path} edge, where the operand's own value ends and a
shared source begins. cover stops there too, through named, so both halves of
the fence end at the same boundary — and two names drawing from one referenced
category stay two draws.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -365,10 +365,12 @@ the reference sits, including in a field the format renders.
|
||||
|
||||
A `{calc()}` operand is held on its own terms too, so the same fence guards it:
|
||||
`{..cat.net} x 2 = {calc(net * 2, 2)}` names one field two ways and is a load
|
||||
error, with no path token anywhere. A calc renders its operand whole, so what the
|
||||
hold pins is that one field — another route conflicts only by naming it. Two
|
||||
names that merely draw from one source are two draws, as `{word} {word}` is, so
|
||||
two dice over one `{..die}` are fine.
|
||||
error, with no path token anywhere. A calc renders its operand whole, so the hold
|
||||
pins every value that render settled: naming the operand and reaching into it
|
||||
both conflict, and `{net.v}` and `{..cat.net.v}` are rejected alike. The hold
|
||||
ends at a `{..path}`, where the operand's own value ends and a shared source
|
||||
begins — two names drawing from one referenced category are two draws, as
|
||||
`{word} {word}` is, so two dice over one `{..die}` are fine.
|
||||
|
||||
```text
|
||||
token {p} renders a level that {p.first} reads a path into; name the fields you want instead
|
||||
|
||||
+19
-4
@@ -103,16 +103,31 @@ func cover(n node, into map[node]bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// operandDraw collects what one held draw of a {calc()} operand answers for: that
|
||||
// one node, since a calc renders its operand whole. Neither wider set works —
|
||||
// containment reaches a sibling the operand never renders, and the render closure
|
||||
// reaches a source two operands share, and neither of those can disagree with it.
|
||||
// operandDraw collects what one held draw of a {calc()} operand answers for: the
|
||||
// operand and what rendering it settles inside itself. A calc renders its operand
|
||||
// whole, so that draw fixes every value the render produced, and a second route to
|
||||
// any of them disagrees with it.
|
||||
//
|
||||
// The walk stops at a {..path} edge, which is where the operand's own value ends
|
||||
// and a shared source begins: two names referencing one category are two draws, the
|
||||
// same rule {word} {word} follows. cover stops there too, by way of named, so both
|
||||
// halves of the fence end at the same boundary. Containment would be wrong here —
|
||||
// it reaches a sibling the operand never renders, which is no part of its value.
|
||||
// A literal is left out for the reason cover leaves one out.
|
||||
func operandDraw(n node, into map[node]bool) {
|
||||
if _, fixed := n.(literal); fixed {
|
||||
return
|
||||
}
|
||||
if into[n] {
|
||||
return
|
||||
}
|
||||
into[n] = true
|
||||
for _, e := range renderEdges(n) {
|
||||
if isRef(e.label) {
|
||||
continue
|
||||
}
|
||||
operandDraw(e.to, into)
|
||||
}
|
||||
}
|
||||
|
||||
// renders reports whether rendering n can reach anything in want, following the
|
||||
|
||||
Reference in New Issue
Block a user