diff --git a/format.go b/format.go index 689f6fc..ab0e60f 100644 --- a/format.go +++ b/format.go @@ -246,7 +246,7 @@ func pathArm(name, head, writtenHead string, segs []string) arm { return arm{spelling: name, head: head, writtenHead: writtenHead, tail: segs, levels: levelKeys(append([]string{head}, segs...), 1)} } -// levelKeys is the key of each level path passes, the first after its first from segments. +// levelKeys keys each prefix of path, from its first from segments to the whole of it. func levelKeys(path []string, from int) []pickKey { levels := make([]pickKey, len(path)-from+1) for i := range levels { diff --git a/link.go b/link.go index 6be0012..fb22c4e 100644 --- a/link.go +++ b/link.go @@ -115,8 +115,8 @@ func nameTargets(ts []linkSite) map[*nameBinding]nameTarget { return targets } -// addressedKeys is every key the reads of each name in ts land on or pass, from the name, with the -// spelling of the first read reaching it. +// addressedKeys is every key the reads of each name in ts land on or pass, from the name's own +// level, with the spelling of the first read reaching it. func addressedKeys(ts []linkSite, targets map[*nameBinding]nameTarget) map[*nameBinding]map[pickKey]string { keys := map[*nameBinding]map[pickKey]string{} for _, s := range ts { diff --git a/perf_test.go b/perf_test.go index 6306c6d..a88314e 100644 --- a/perf_test.go +++ b/perf_test.go @@ -64,6 +64,7 @@ func TestNoReferenceAllocRegression(t *testing.T) { }{ {"a repeat of a reference path", `{"format":"{r}","r":{"format":"{/word.w}","repeat":20,"separator":", "}}`, 25}, {"a repeat of a name", `{"format":"{r}","r":{"format":"{/word as n}{n.w}{n.w}","repeat":20,"separator":", "}}`, 162}, + {"a read under a nested pick", `{"format":"{place.geo as g}{g} {g.town.zip}","place":{"format":"{geo}","geo":{"format":"{town.zip}","town":{"format":"{zip}","zip":"1"}}}}`, 10}, } { f, err := New(WithoutShippedData(), WithDataFS(fstest.MapFS{ "word.json": {Data: []byte(word)}, diff --git a/pick.go b/pick.go index 3d01a0b..eb537be 100644 --- a/pick.go +++ b/pick.go @@ -10,8 +10,8 @@ import ( // the pick. type pickKey string -// under is the key of rel, a fresh read's key, in what renders at k. It never returns k itself: k -// is sc.pickAt, and a memo keeping it would move every render's scope to the heap. +// under is the key of rel, a fresh read's key, in what renders at k. It never returns k itself: on +// a render k is sc.pickAt, and a memo keeping it would move every render's scope to the heap. func (k pickKey) under(rel pickKey) pickKey { if k == "" { return rel @@ -20,8 +20,7 @@ func (k pickKey) under(rel pickKey) pickKey { } // namedPick is one draw of a name: the variant drawn at each level a read of it addresses, the -// value each read there produced, keyed by the path from the name, and the table rows they -// pinned. +// value each read there produced, by its pickKey, and the table rows they pinned. type namedPick struct { named *nameBinding memo drawMemo @@ -29,7 +28,7 @@ type namedPick struct { } // drawMemo is what a named pick has drawn: the variant each level was drawn as, so every path -// under it reads one variant; the value each read produced, by its path, so the same read written +// under it reads one variant; the value each read produced, by its pickKey, so the same read written // twice reads one value; the frames a read opens for the name scopes around where it lands // (enter); and the rows drawn by each step down after a "..", by level. type drawMemo struct { @@ -202,9 +201,10 @@ func readUnder(s *drawstate.State, t *template, sc renderScope, a arm) readValue return r } levels := make([]pickKey, len(a.levels)) - for i, l := range a.levels { + for i, l := range a.levels[:len(levels)-1] { levels[i] = sc.pickAt.under(l) } + levels[len(levels)-1] = key leaf, pins := p.draw(s, t.head(a.head), a.steps, levels) return p.renderAt(s, leaf, pins, key, sc) } diff --git a/render.go b/render.go index 1396bcf..6b58fb9 100644 --- a/render.go +++ b/render.go @@ -146,8 +146,7 @@ func expand(s *drawstate.State, t *template, sc renderScope) string { // a level that a read of the name being rendered addresses; every other read draws afresh, so // {word} {word} draws twice and {p.a} {p.b} reads two draws of p. // A fresh read clears sc.pick. Kept, a read nested in the fresh one would key the pick's memo by -// sc.pickAt, where in the pick it renders, and share a value with a different -// node at that path. +// sc.pickAt, where in the pick it renders, and share a value with a different node at that key. func readField(s *drawstate.State, t *template, sc renderScope, a arm) readValue { switch { case a.kind == namedRead: