From 7baee64cc300bb5ec251a423d0dc13f9d8dcf903 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Tue, 29 Sep 2026 11:13:33 +0200 Subject: [PATCH] Move each draw fence beside the check it pairs with, and inline checkColumnDraws --- README.md | 6 ++--- drawfence.go | 69 +-------------------------------------------------- node.go | 30 ++++++++++++++++++++++ readsurvey.go | 35 ++++++++++++++++++++++++++ todo.md | 7 +----- 5 files changed, 70 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 5867104..9b865f9 100644 --- a/README.md +++ b/README.md @@ -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 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 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 @@ -885,8 +885,8 @@ inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplat 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 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 -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 +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, a table reaching its own family included 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 graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences diff --git a/drawfence.go b/drawfence.go index 27cef1a..8f46f5d 100644 --- a/drawfence.go +++ b/drawfence.go @@ -2,36 +2,6 @@ package fejkdata 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 // reference path. // 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 } -// 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 // 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 @@ -123,7 +58,7 @@ func (f *drawFence) checkRecordDraws(path string, n node) error { if !reads { 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 nil @@ -186,5 +121,3 @@ func readsTable(n node, a arm) bool { _, isTable := t.head(a.head).(*table) return isTable } - -func checkColumnDraws(t *template, columns []string) error { return surveyColumns(t, columns).check() } diff --git a/node.go b/node.go index 3315751..ebb52b2 100644 --- a/node.go +++ b/node.go @@ -427,6 +427,36 @@ func repeatOf(m map[string]any) (int, error) { 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 // iteration renders in no draw group. func drawGroupOf(m map[string]any, repeat int) (string, error) { diff --git a/readsurvey.go b/readsurvey.go index 6c48de5..e08dba1 100644 --- a/readsurvey.go +++ b/readsurvey.go @@ -248,6 +248,41 @@ func checkFamilies(reads []pathRead) error { 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 // table one read draws that the other pins, since which token renders first would // then decide the row. diff --git a/todo.md b/todo.md index 05a2df2..793ffdf 100644 --- a/todo.md +++ b/todo.md @@ -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: -- 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 to its first reader and the path it starts, if any: four seats traced `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`. - 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`, - `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. - 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.