From aed74498e6f6e3f0c5e66637a9cb700c6e43ca13 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 21 Sep 2026 10:22:42 +0200 Subject: [PATCH] Name every render start, and let no backticked spelling go unchecked --- README.md | 2 +- doc.go | 19 ++++++++------ doc_test.go | 71 +++++++++++++++++++++++++++++------------------------ 3 files changed, 51 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 1f01cfe..08132e0 100644 --- a/README.md +++ b/README.md @@ -1309,7 +1309,7 @@ the Development section below, and who ships a register the four above then draw ## Layout ``` -doc.go the package doc, and the vocabulary the rest of it uses for a render and its fences +doc.go the package doc, and the vocabulary the package is written in fejkdata.go Generator, New, options, the embedded data set, List node.go the node model and JSON -> node compilation table.go tables: the rows TSV, its options and links, row selection and draws diff --git a/doc.go b/doc.go index 16a8142..667b40c 100644 --- a/doc.go +++ b/doc.go @@ -19,9 +19,11 @@ // the pick produced: `pick` and `drawn` settle a choice, `table.draw` a row, and // `draw` carries what a read produced. // - expansion — one render of one format, by `expand`. A nested template is an -// expansion of its own, and so is each iteration of a `template.repeat`. -// - render — one `Fake` call or one record, started by `renderOnce` where nothing -// outer started it. It owns one `drawSet`, so it is what a reference draw spans. +// expansion of its own. +// - render — what owns one `drawSet`, and so what one reference draw spans: +// `Generator.Fake`, `FakeRecord`, `FakeStruct`, `Template.Fake` and +// `RecordTemplate.Fake` each start one, as does `expandAnew` per iteration of a +// `template.repeat` — an iteration is an expansion and a render of its own. // - hold — keeping one draw of a name, so every route to it reads that value. // `template.held` lists what an expansion holds, `draws` keeps them and // `readField` reads through it; a reference path is held for the whole render. @@ -32,10 +34,11 @@ // rows of its ancestors, and `draws.rowOf` draws one where none is pinned. // - family — a table and every table reaching it through a chain of // `table.parentT`, named by the root that chain ends at: `table.family`. -// - whole — a read that lands on a `row`, so the table's format renders every -// column of it: `tableRead.whole`. +// - whole — a read that lands on a `row`, so the row renders through its table's +// format: `tableRead.whole` says a read did, `table.whole` is the node. // - fence — a load-time check refusing data that two routes to one draw would -// disagree in. Every one runs in `New`, which is why `Fake` cannot fail on -// loaded data: `heldCheck` over an expansion's held names, `drawCheck` over a -// render's reference paths, and `checkFamilies` over the rows they pin. +// disagree in, which is why a render cannot fail: `heldCheck` over an +// expansion's held names, `drawCheck` over a render's reference paths, and +// `checkFamilies` over the rows they pin. `New` runs them over the data set, +// `NewTemplate` and `FakeStruct` over what those compile. package fejkdata diff --git a/doc_test.go b/doc_test.go index 30066e1..56124f1 100644 --- a/doc_test.go +++ b/doc_test.go @@ -2,18 +2,20 @@ package fejkdata import ( "go/ast" + "go/build" "go/parser" "go/token" - "os" "regexp" "strings" "testing" ) -var backticked = regexp.MustCompile("`([A-Za-z][A-Za-z0-9]*(?:\\.[A-Za-z][A-Za-z0-9]*)?)`") +// Every backtick in the Vocabulary is a symbol, so an unrecognised spelling fails +// rather than going unchecked. +var backticked = regexp.MustCompile("`([^`]+)`") -// TestVocabularyNamesDeclaredSymbols proves every symbol the package doc's Vocabulary -// names is declared here, so a renamed unit renames its entry with it. +// TestVocabularyNamesDeclaredSymbols proves the package doc's Vocabulary names +// symbols this package declares, so a renamed unit renames its entry with it. func TestVocabularyNamesDeclaredSymbols(t *testing.T) { files := sourceFiles(t) declared := declaredSymbols(files) @@ -24,21 +26,15 @@ func TestVocabularyNamesDeclaredSymbols(t *testing.T) { } } -// sourceFiles parses the package's own files, the tests left out: the Vocabulary -// indexes what ships, not what proves it. func sourceFiles(t *testing.T) []*ast.File { t.Helper() - entries, err := os.ReadDir(".") + pkg, err := build.ImportDir(".", 0) if err != nil { t.Fatalf("read the package directory: %v", err) } fset := token.NewFileSet() var files []*ast.File - for _, e := range entries { - name := e.Name() - if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") { - continue - } + for _, name := range pkg.GoFiles { f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) if err != nil { t.Fatalf("parse %s: %v", name, err) @@ -48,7 +44,6 @@ func sourceFiles(t *testing.T) []*ast.File { return files } -// vocabulary is the package doc from its Vocabulary heading on. func vocabulary(t *testing.T, files []*ast.File) string { t.Helper() for _, f := range files { @@ -63,45 +58,57 @@ func vocabulary(t *testing.T, files []*ast.File) string { return "" } -// declaredSymbols is every name the package declares, a method and a struct field -// under its type as well: fence, draws.pin, tableRead.whole. +// declaredSymbols is every name the package declares at the top level, a method and +// a struct field keyed under its type: draws.pin, tableRead.whole. A name declared +// inside a function body is not one a Vocabulary entry may name. func declaredSymbols(files []*ast.File) map[string]bool { names := map[string]bool{} for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - switch d := n.(type) { + for _, decl := range f.Decls { + switch d := decl.(type) { case *ast.FuncDecl: names[d.Name.Name] = true - if d.Recv != nil { - names[receiverType(d.Recv)+"."+d.Name.Name] = true - } - case *ast.TypeSpec: - names[d.Name.Name] = true - addFields(names, d) - case *ast.ValueSpec: - for _, id := range d.Names { - names[id.Name] = true + if recv := receiverType(d.Recv); recv != "" { + names[recv+"."+d.Name.Name] = true } + case *ast.GenDecl: + addSpecs(names, d) } - return true - }) + } } return names } -func addFields(names map[string]bool, d *ast.TypeSpec) { - s, isStruct := d.Type.(*ast.StructType) +func addSpecs(names map[string]bool, d *ast.GenDecl) { + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + names[s.Name.Name] = true + addFields(names, s) + case *ast.ValueSpec: + for _, id := range s.Names { + names[id.Name] = true + } + } + } +} + +func addFields(names map[string]bool, s *ast.TypeSpec) { + st, isStruct := s.Type.(*ast.StructType) if !isStruct { return } - for _, field := range s.Fields.List { + for _, field := range st.Fields.List { for _, id := range field.Names { - names[d.Name.Name+"."+id.Name] = true + names[s.Name.Name+"."+id.Name] = true } } } func receiverType(recv *ast.FieldList) string { + if recv == nil || len(recv.List) == 0 { + return "" + } e := recv.List[0].Type if star, isPointer := e.(*ast.StarExpr); isPointer { e = star.X