Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
5 changed files with 70 additions and 77 deletions
Showing only changes of commit 7baee64cc3 - Show all commits
+3 -3
View File
@@ -875,7 +875,7 @@ the Development section below, and who ships a register the five above then draw
``` ```
doc.go the package doc, and the vocabulary the package is written in doc.go the package doc, and the vocabulary the package is written in
fejkdata.go Generator, New, options, the embedded data set, List fejkdata.go Generator, New, options, the embedded data set, List
node.go the node model and JSON -> node compilation node.go the node model and JSON -> node compilation, with its fence on a nested drawGroup
table.go tables: the rows TSV, its options and links, row selection and draws table.go tables: the rows TSV, its options and links, row selection and draws
path.go the dotted-path walks with their selectors, and proving a path resolves path.go the dotted-path walks with their selectors, and proving a path resolves
render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make
@@ -885,8 +885,8 @@ inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplat
format.go the {token} grammar: scanning, tokens, operands, validation, compiling a format format.go the {token} grammar: scanning, tokens, operands, validation, compiling a format
hold.go the hold: one draw per held name; and the hold set and scope a render reads its reference paths through hold.go the hold: one draw per held name; and the hold set and scope a render reads its reference paths through
holdfence.go the load fences over the hold: the routes and spellings that read one held name two ways holdfence.go the load fences over the hold: the routes and spellings that read one held name two ways
drawfence.go the load fences over reference draws: a drawGroup's, a table's own family, and a render's and a record's columns through the survey drawfence.go the load fences over reference draws: a drawGroup's, and a render's and a record's columns through the survey
readsurvey.go the load-time survey of what a render reads, and what a path reads of a table family, and its verdict on them readsurvey.go the load-time survey of what a render reads, and what a path reads of a table family, and its verdict on them, a table reaching its own family included
pins.go the pin set: the table rows a render fixes, which the draw fences replay pins.go the pin set: the table rows a render fixes, which the draw fences replay
reference.go reference sigils, and binding references across the tree reference.go reference sigils, and binding references across the tree
graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences
+1 -68
View File
@@ -2,36 +2,6 @@ package fejkdata
import "fmt" import "fmt"
// checkNestedDrawGroup refuses a template beneath one drawing in group that names group again,
// short of a repeat or another draw group.
func checkNestedDrawGroup(fields map[string]node, group string) error {
if group == "" {
return nil
}
var walk func(path string, n node) error
walk = func(path string, n node) error {
t, isTemplate := n.(*template)
switch {
case isTemplate && t.drawGroup == group:
return fmt.Errorf("%q names drawGroup %q, the draw group this template draws in already; drop it", path, group)
case isTemplate && (t.drawGroup != "" || t.repeat > 1):
return nil
}
for _, c := range contained(n) {
if err := walk(join(path, c.name), c.node); err != nil {
return err
}
}
return nil
}
for _, name := range sortedNames(fields) {
if err := walk(name, fields[name]); err != nil {
return err
}
}
return nil
}
// drawFence fences each template of a scope as a render of its own, remembering which nodes read a // drawFence fences each template of a scope as a render of its own, remembering which nodes read a
// reference path. // reference path.
// docs/decisions.md#a-render-shares-one-reference-draw-per-category-per-group // docs/decisions.md#a-render-shares-one-reference-draw-per-category-per-group
@@ -71,41 +41,6 @@ func (f *drawFence) checkDraws(path string, n node) error {
return nil return nil
} }
// checkOwnFamily refuses a table's format or cell that reads, however many templates
// away and through a repeat or a draw group too, a table of its own family: a whole
// read draws its row without pinning it, so the family would draw apart from the row
// it renders, whichever draws the reaching template holds.
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
func checkOwnFamily(t *template) error {
own := t.site.table
if own == nil {
return nil
}
seen := map[node]bool{}
var find func(n node) (renderEdge, *table, bool)
find = func(n node) (renderEdge, *table, bool) {
if seen[n] {
return renderEdge{}, nil, false
}
seen[n] = true
for _, e := range renderEdges(n) {
if e.readsRef() {
if familyTable, isTable := n.(*template).head(e.read.head).(*table); isTable && familyTable.familyRoot() == own.familyRoot() {
return e, familyTable, true
}
}
if e, familyTable, found := find(e.to); found {
return e, familyTable, true
}
}
return renderEdge{}, nil, false
}
if e, familyTable, found := find(t); found {
return fmt.Errorf("%s reads %s, a table of its own family, which a bare read of %s would draw apart from the row it renders; read the family from a template beside it, or add the value as a column", e.reached(), familyTable.segment, own.segment)
}
return nil
}
// checkRecordDraws fences the columns of a record — a template compiled at the top without a // checkRecordDraws fences the columns of a record — a template compiled at the top without a
// repeat — so a load proves the record view of it as well as the string view. // repeat — so a load proves the record view of it as well as the string view.
// docs/decisions.md#a-category-never-references-itself-and-a-records-fences-run-at-load // docs/decisions.md#a-category-never-references-itself-and-a-records-fences-run-at-load
@@ -123,7 +58,7 @@ func (f *drawFence) checkRecordDraws(path string, n node) error {
if !reads { if !reads {
return nil return nil
} }
if err := checkColumnDraws(t, columns); err != nil { if err := surveyColumns(t, columns).check(); err != nil {
return fmt.Errorf("%s: %w", path, err) return fmt.Errorf("%s: %w", path, err)
} }
return nil return nil
@@ -186,5 +121,3 @@ func readsTable(n node, a arm) bool {
_, isTable := t.head(a.head).(*table) _, isTable := t.head(a.head).(*table)
return isTable return isTable
} }
func checkColumnDraws(t *template, columns []string) error { return surveyColumns(t, columns).check() }
+30
View File
@@ -427,6 +427,36 @@ func repeatOf(m map[string]any) (int, error) {
return int(r), nil return int(r), nil
} }
// checkNestedDrawGroup refuses a template beneath one drawing in group that names group again,
// short of a repeat or another draw group.
func checkNestedDrawGroup(fields map[string]node, group string) error {
if group == "" {
return nil
}
var walk func(path string, n node) error
walk = func(path string, n node) error {
t, isTemplate := n.(*template)
switch {
case isTemplate && t.drawGroup == group:
return fmt.Errorf("%q names drawGroup %q, the draw group this template draws in already; drop it", path, group)
case isTemplate && (t.drawGroup != "" || t.repeat > 1):
return nil
}
for _, c := range contained(n) {
if err := walk(join(path, c.name), c.node); err != nil {
return err
}
}
return nil
}
for _, name := range sortedNames(fields) {
if err := walk(name, fields[name]); err != nil {
return err
}
}
return nil
}
// drawGroupOf reads a template's "drawGroup" (default ""), which a repeat cannot carry: each // drawGroupOf reads a template's "drawGroup" (default ""), which a repeat cannot carry: each
// iteration renders in no draw group. // iteration renders in no draw group.
func drawGroupOf(m map[string]any, repeat int) (string, error) { func drawGroupOf(m map[string]any, repeat int) (string, error) {
+35
View File
@@ -248,6 +248,41 @@ func checkFamilies(reads []pathRead) error {
return replayPairs(reads) return replayPairs(reads)
} }
// checkOwnFamily refuses a table's format or cell that reads, however many templates
// away and through a repeat or a draw group too, a table of its own family: a whole
// read draws its row without pinning it, so the family would draw apart from the row
// it renders, whichever draws the reaching template holds.
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
func checkOwnFamily(t *template) error {
own := t.site.table
if own == nil {
return nil
}
seen := map[node]bool{}
var find func(n node) (renderEdge, *table, bool)
find = func(n node) (renderEdge, *table, bool) {
if seen[n] {
return renderEdge{}, nil, false
}
seen[n] = true
for _, e := range renderEdges(n) {
if e.readsRef() {
if familyTable, isTable := n.(*template).head(e.read.head).(*table); isTable && familyTable.familyRoot() == own.familyRoot() {
return e, familyTable, true
}
}
if e, familyTable, found := find(e.to); found {
return e, familyTable, true
}
}
return renderEdge{}, nil, false
}
if e, familyTable, found := find(t); found {
return fmt.Errorf("%s reads %s, a table of its own family, which a bare read of %s would draw apart from the row it renders; read the family from a template beside it, or add the value as a column", e.reached(), familyTable.segment, own.segment)
}
return nil
}
// checkFamilyPair refuses a table read whole beside a path into its family, and a // checkFamilyPair refuses a table read whole beside a path into its family, and a
// table one read draws that the other pins, since which token renders first would // table one read draws that the other pins, since which token renders first would
// then decide the row. // then decide the row.
+1 -6
View File
@@ -31,11 +31,6 @@ on 2026-09-29 a third time (Navigation 7.0, Locality 5.6, Shape 6.3, Self-suffic
This round, in order: This round, in order:
- Move `checkOwnFamily` into `readsurvey.go` beside `checkFamilies`, and
`checkNestedDrawGroup` into `node.go` beside `drawGroupOf`, the compile step calling
it; delete `checkColumnDraws` for `surveyColumns(…).check()` at its caller. Both
architects found the family fence in two files, and draw-group checks of two
lifecycles in one.
- Replace `formatOps`' `held`, `holder` and `pathHeads` with one map from a held name - Replace `formatOps`' `held`, `holder` and `pathHeads` with one map from a held name
to its first reader and the path it starts, if any: four seats traced to its first reader and the path it starts, if any: four seats traced
`formatOps.holdName` writing four fields at once. `formatOps.holdName` writing four fields at once.
@@ -332,7 +327,7 @@ because pairing a street with its exact postnummer rewrites shipped rows.
records `Navigation`, `Locality`, `Shape` and `Self-sufficiency`. records `Navigation`, `Locality`, `Shape` and `Self-sufficiency`.
- Delete `doc.go`'s "The draw fences are …" sentence: it names a function, a type and - Delete `doc.go`'s "The draw fences are …" sentence: it names a function, a type and
a helper reached only through `readSurvey.check`, and misses `checkDrawGroup`, a helper reached only through `readSurvey.check`, and misses `checkDrawGroup`,
`checkOwnFamily` and `checkColumnDraws`; the README's Layout indexes the fences by file. `checkOwnFamily` and `checkNestedDrawGroup`; the README's Layout indexes the fences by file.
Say "fences" in the `pin` entry then, which names the draw fences too. Say "fences" in the `pin` entry then, which names the draw fences too.
- Cut the README's Layout block to the lines that say what a file name cannot: - Cut the README's Layout block to the lines that say what a file name cannot:
`calc.go`, `doc.go` and `cmd/fejkdata/` restate their names. `calc.go`, `doc.go` and `cmd/fejkdata/` restate their names.