Answer stability round 1: readUnder builds the leaf key once, the pick's comments name the pickKey, and the allocation gate covers a read under a nested pick
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)},
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user