From 7e073462c89d551bc2bdd0c40866a940e8732292 Mon Sep 17 00:00:00 2001 From: M Date: Mon, 31 Aug 2026 14:03:07 +0200 Subject: [PATCH] Document the fence a calc operand gets, and trim two comments The one-spelling section described a calc operand only as a route that renders a level, so a reader could not predict that {..cat.net} beside {calc(net * 2, 2)} is now a load error with no path token in sight. It says so, and says what an operand's draw covers and what it leaves free. Dropped the paragraph in checkBoundLevelsHeld that restated its own first line and the loop below it, and the escape-analysis note in expand. Co-Authored-By: Claude Opus 5 --- README.md | 6 ++++++ reference.go | 3 --- render.go | 3 +-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7b9075a..b565a2b 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,12 @@ always agrees, and the other is a load error. This covers every way a level can be rendered: a token, a `{calc()}` operand, and a `{..path}` reference — wherever 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. What an operand's draw fixes is the value it +renders, so a reference reaching *through* it is caught as well, while a sibling +its format never renders is free — that sibling is no part of the value. + ```text token {p} renders a level that {p.first} reads a path into; name the fields you want instead ``` diff --git a/reference.go b/reference.go index 2d4d5b6..b33cee9 100644 --- a/reference.go +++ b/reference.go @@ -44,9 +44,6 @@ func linkRefs(root map[string]node) error { // format (a token, a calc operand); this settles the rest — a reference, whether it // sits in that format or in anything the format renders, however deep. // -// It walks t.held, so it covers both kinds of hold: the level a dotted token reads, -// and the sibling a {calc()} reads. -// // It runs after checkNoCycles, whose guarantee is what lets the walk terminate. func checkBoundLevelsHeld(root map[string]node) error { return walkNodes(root, func(path string, n node) error { diff --git a/render.go b/render.go index d8e6c91..e5b2952 100644 --- a/render.go +++ b/render.go @@ -138,8 +138,7 @@ func expand(s *session, t *template) string { var b strings.Builder b.Grow(t.grow) // One draw per held name, for this expansion only: a nested template and each - // repeat iteration get their own, since each is its own expansion. held reaches - // no further than readField, which keeps it and its maps on the stack. + // repeat iteration get their own, since each is its own expansion. var held *draws if len(t.held) > 0 { held = &draws{