From cc8797744bf3c913a3a15515f78d117840f6c3a9 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 3 Oct 2026 17:46:28 +0200 Subject: [PATCH] Answer architecture round 3: the generated header names go generate, one sorted-keys helper everywhere, one site constructor, and an inline folder reference refused before anything loads --- data.go | 6 +++++- fejkdata.go | 7 +------ inline.go | 23 ++++++++++++++++++----- namefence.go | 8 +------- naming_test.go | 7 +------ path.go | 7 +------ shape_test.go | 8 +------- shipped.go | 2 +- shipped_test.go | 4 ++-- shippedindex.go | 2 +- 10 files changed, 32 insertions(+), 42 deletions(-) diff --git a/data.go b/data.go index 9a88b9a..cb31d96 100644 --- a/data.go +++ b/data.go @@ -203,13 +203,17 @@ func categorySites(root *folder) []categorySite { walk(append(dir[:len(dir):len(dir)], name), sub) continue } - out = append(out, categorySite{dir: dir, path: join(strings.Join(dir, "."), name), in: g, n: g.children[name]}) + out = append(out, siteIn(dir, g, name)) } } walk(nil, root) return out } +func siteIn(dir []string, in *folder, name string) categorySite { + return categorySite{dir: dir, path: join(strings.Join(dir, "."), name), in: in, n: in.children[name]} +} + func sitesScope(sites []categorySite) nodeScope { return func(fn func(path string, n node) error) error { for _, s := range sites { diff --git a/fejkdata.go b/fejkdata.go index cfccc77..660cbb5 100644 --- a/fejkdata.go +++ b/fejkdata.go @@ -180,12 +180,7 @@ func paths(n node) []string { func tablePaths(t *table) []string { out := append([]string{""}, t.header...) sort.Strings(out[1:]) - children := make([]string, 0, len(t.children)) - for name := range t.children { - children = append(children, name) - } - sort.Strings(children) - for _, name := range children { + for _, name := range sortedNames(t.children) { for _, p := range paths(t.children[name]) { out = append(out, join(name, p)) } diff --git a/inline.go b/inline.go index 5e2a2a8..14d4e6d 100644 --- a/inline.go +++ b/inline.go @@ -152,6 +152,9 @@ func inputValue(input string) (any, error) { // binding's. func bindInline(root *folder, n node, label string, typedByGo bool) error { scope := inlineScope(n, label) + if err := refuseFolderRefs(scope); err != nil { + return err + } loadShipped(root, unloadedReads(root, nil, scope)) return binding{ scope: scope, @@ -166,10 +169,9 @@ func bindInline(root *folder, n node, label string, typedByGo bool) error { }.bind() } -// linkNodeRefs binds the references in an inline node's templates against the -// loaded tree. -func linkNodeRefs(scope nodeScope, root map[string]node) error { - if err := scope(func(path string, m node) error { +// refuseFolderRefs refuses a reference an inline node writes from a folder, which it has none of. +func refuseFolderRefs(scope nodeScope) error { + return scope(func(path string, m node) error { t, ok := m.(*template) if !ok { return nil @@ -183,7 +185,18 @@ func linkNodeRefs(scope nodeScope, root map[string]node) error { return fmt.Errorf("%s: reference {%s}: an inline template has no folder; write {/%s}", path, name, rest) } } - return linkTemplate(nil, path, "", t, root) + return nil + }) +} + +// linkNodeRefs binds the references in an inline node's templates against the +// loaded tree. +func linkNodeRefs(scope nodeScope, root map[string]node) error { + if err := scope(func(path string, m node) error { + if t, ok := m.(*template); ok { + return linkTemplate(nil, path, "", t, root) + } + return nil }); err != nil { return err } diff --git a/namefence.go b/namefence.go index 52dac90..f99765d 100644 --- a/namefence.go +++ b/namefence.go @@ -3,7 +3,6 @@ package fejkdata import ( "fmt" "slices" - "sort" ) // checkNameReads refuses each binding of t that checkUses refuses, and a read such as {n} whose @@ -117,12 +116,7 @@ func (b *nameBinding) checkTemplateOnce(read string, t *template, key string) er } } } - heads := make([]string, 0, len(fresh)) - for head := range fresh { - heads = append(heads, head) - } - sort.Strings(heads) - for _, head := range heads { + for _, head := range sortedNames(fresh) { if by, kept := b.addressed[join(key, head)]; kept && fresh[head] > 1 { return fmt.Errorf("{%s} renders field %q twice, so {%s} cannot say which draw it reads; drop {%s} or {%s}", read, head, by, read, by) } diff --git a/naming_test.go b/naming_test.go index b7c3e38..84c80ea 100644 --- a/naming_test.go +++ b/naming_test.go @@ -11,12 +11,7 @@ import ( // docs/decisions.md#one-name-one-meaning func TestNoFunctionSpellsAMethod(t *testing.T) { funcs, methods := declaredCalls(namespaceFiles(t)) - names := make([]string, 0, len(funcs)) - for name := range funcs { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { + for _, name := range sortedNames(funcs) { if recv := methods[name]; len(recv) > 0 { sort.Strings(recv) t.Errorf("func %s is spelled by %s too; name them apart", name, strings.Join(recv, " and ")) diff --git a/path.go b/path.go index eabadcb..bcb8747 100644 --- a/path.go +++ b/path.go @@ -2,7 +2,6 @@ package fejkdata import ( "fmt" - "sort" "strings" ) @@ -470,11 +469,7 @@ func unreachableInChoice(c *choice, want string) error { if len(c.shared) == 0 { return fmt.Errorf("no variant of this %d-way choice carries %q", len(c.items), want) } - offered := make([]string, 0, len(c.shared)) - for p := range c.shared { - offered = append(offered, p) - } - sort.Strings(offered) + offered := sortedNames(c.shared) return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered) } diff --git a/shape_test.go b/shape_test.go index a912b67..42e6299 100644 --- a/shape_test.go +++ b/shape_test.go @@ -2,7 +2,6 @@ package fejkdata import ( "os" - "sort" "strconv" "strings" "testing" @@ -109,12 +108,7 @@ func readsFact(n node) string { if len(set) == 0 { return "" } - keys := make([]string, 0, len(set)) - for k := range set { - keys = append(keys, k) - } - sort.Strings(keys) - return "\treads " + strings.Join(keys, " ") + return "\treads " + strings.Join(sortedNames(set), " ") } func TestShippedShapeNamesReads(t *testing.T) { diff --git a/shipped.go b/shipped.go index 0783f4c..ffba5b5 100644 --- a/shipped.go +++ b/shipped.go @@ -139,7 +139,7 @@ func (u unloadedCategory) load() (categorySite, error) { return categorySite{}, err } delete(u.in.unloaded, u.name) - return categorySite{dir: u.dir, path: join(strings.Join(u.dir, "."), u.name), in: u.in, n: u.in.children[u.name]}, nil + return siteIn(u.dir, u.in, u.name), nil } // linkedTables is the tables beside u that link to it or that it links to: its parent, and diff --git a/shipped_test.go b/shipped_test.go index 61c9f45..a40992f 100644 --- a/shipped_test.go +++ b/shipped_test.go @@ -66,7 +66,7 @@ func TestShippedIndexIsCurrent(t *testing.T) { func shippedIndexSource(whole *Generator) ([]byte, error) { var b bytes.Buffer - b.WriteString("// Code generated by TestShippedIndexIsCurrent with REPIN=1. DO NOT EDIT.\n\npackage fejkdata\n\nvar shippedIndex = map[string]shippedEntry{\n") + b.WriteString("// Code generated by go generate. DO NOT EDIT.\n\npackage fejkdata\n\nvar shippedIndex = map[string]shippedEntry{\n") for _, s := range categorySites(&whole.root) { fmt.Fprintf(&b, "%q: {", s.path) if t, isTable := s.n.(*table); isTable && t.parentIndex >= 0 { @@ -183,7 +183,7 @@ func TestOnDemandRendersAsTheWholeLoad(t *testing.T) { } func TestEveryShippedCategoryLoadsAlone(t *testing.T) { - for p := range shippedIndex { + for _, p := range sortedNames(shippedIndex) { f, err := New() if err != nil { t.Fatalf("New() = %v", err) diff --git a/shippedindex.go b/shippedindex.go index 6e73c43..32ef0e2 100644 --- a/shippedindex.go +++ b/shippedindex.go @@ -1,4 +1,4 @@ -// Code generated by TestShippedIndexIsCurrent with REPIN=1. DO NOT EDIT. +// Code generated by go generate. DO NOT EDIT. package fejkdata