From 26b35abe7064cbdc85050103103957a17afaa742 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 30 Sep 2026 20:35:41 +0200 Subject: [PATCH] Fix at compile how readField reads an arm, deleting its two panics and its held lookup per read --- format.go | 10 ++++++++++ reference.go | 6 ++++++ render.go | 10 ++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/format.go b/format.go index 2d3d4e3..c9f44d6 100644 --- a/format.go +++ b/format.go @@ -315,8 +315,18 @@ type arm struct { steps []pathStep leaves []node // every node the path may land on, one per variant it passes cover node // what holding the path pins: the first choice it passes, else its leaf + kind armKind } +// armKind is how expand reads an arm, fixed at compile. +type armKind uint8 + +const ( + freshRead armKind = iota // drawn afresh at every read + heldRead // drawn once per expansion, kept in its hold + refPathRead // drawn once per draw group, kept in its memo +) + func (a arm) isRefPath() bool { return isRef(a.head) && len(a.tail) > 0 } diff --git a/reference.go b/reference.go index 93c58c5..8eae402 100644 --- a/reference.go +++ b/reference.go @@ -112,6 +112,12 @@ func compileArm(t *template, a *arm) { } w := compilePath(head, a.tail) a.steps, a.leaves, a.cover = w.steps, w.leaves, w.cover + switch _, held := t.compiled.held[a.head]; { + case a.isRefPath(): + a.kind = refPathRead + case held: + a.kind = heldRead + } } // resolveLink binds every reference t reads, refusing one to t's own category, and keys diff --git a/render.go b/render.go index 3982afd..abc02b6 100644 --- a/render.go +++ b/render.go @@ -158,21 +158,15 @@ func expand(s *generatorState, t *template, sc renderScope) string { // read one row, either read twice gives one value, and a shown operand is the operand // computed. Every other name is drawn afresh, so {word} {word} still draws twice. func readField(s *generatorState, t *template, hold *drawMemo, sc renderScope, a arm) readValue { - if a.isRefPath() { + if a.kind == refPathRead { return readReference(s, t, sc, a) } if sc.draws.trace != nil { traceRead(sc.draws.trace, t, sc, a) } - if _, expansionHolds := t.compiled.held[a.head]; !expansionHolds { - if len(a.tail) > 0 { - panic(internalError("%q reads a path into %q, which the expansion does not hold", a.spelling, a.head)) - } + if a.kind == freshRead { return readValue{text: render(s, t.head(a.head), sc)} } - if hold == nil { - panic(internalError("%q reads a name the expansion holds, with no hold to keep it in", a.spelling)) - } return readMemo(s, t, hold, nil, sc, a) }