Fail loudly where calc says it cannot happen

calcVar.eval returned NaN on a path indexVars makes unreachable, and a name
missing from the index map read operand 0. Both would have printed a wrong
number rather than reporting anything. They panic now, like child() does for
the same class of slip, so drift between calcVars and indexVars surfaces as a
stack trace naming the operand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
M
2026-08-31 13:42:41 +02:00
committed by lilleman-tw
parent 1ba9185edc
commit 1b5e928d4f
+11 -3
View File
@@ -44,8 +44,12 @@ func (n calcIdx) eval(operands []string) float64 {
} }
// eval on an unplaced name cannot happen: calcPrep runs indexVars over every // eval on an unplaced name cannot happen: calcPrep runs indexVars over every
// expression it compiles, so only a calcIdx reaches a render. // expression it compiles, so only a calcIdx reaches a render. It panics rather
func (n calcVar) eval([]string) float64 { return math.NaN() } // than returning NaN, so a node kind indexVars forgets is a stack trace and not a
// silently wrong number.
func (n calcVar) eval([]string) float64 {
panic(fmt.Sprintf("fakes: calc operand %q was never placed", string(n)))
}
func (n calcNeg) eval(operands []string) float64 { return -n.x.eval(operands) } func (n calcNeg) eval(operands []string) float64 { return -n.x.eval(operands) }
@@ -112,7 +116,11 @@ func calcPrep(args []string) callFn {
func indexVars(n calcNode, at map[string]int) calcNode { func indexVars(n calcNode, at map[string]int) calcNode {
switch n := n.(type) { switch n := n.(type) {
case calcVar: case calcVar:
return calcIdx(at[string(n)]) i, placed := at[string(n)]
if !placed { // calcVars named every operand, so a miss means the two disagree
panic(fmt.Sprintf("fakes: calc operand %q is not among the names read for it", string(n)))
}
return calcIdx(i)
case calcNeg: case calcNeg:
return calcNeg{indexVars(n.x, at)} return calcNeg{indexVars(n.x, at)}
case calcBin: case calcBin: