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
8 changed files with 23 additions and 32 deletions
Showing only changes of commit e9bb89f044 - Show all commits
+1 -1
View File
@@ -870,7 +870,7 @@ 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
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 walk with its selectors, and proving a path resolves path.go the dotted-path walks with their selectors, and proving a path resolves
render.go Fake and the recursive renderer (choices, format strings, expansions) render.go Fake and the recursive renderer (choices, format strings, expansions)
record.go records: Record, the JSON/CSV/SQL serializers, and their entry points record.go records: Record, the JSON/CSV/SQL serializers, and their entry points
struct.go structs: FakeStruct, fake tags, and a field's Go type as its column's datatype struct.go structs: FakeStruct, fake tags, and a field's Go type as its column's datatype
+3 -3
View File
@@ -325,7 +325,7 @@ Go ties a package to one directory, so folders would split the API into packages
## The performance gate asserts allocations, not wall-clock time ## The performance gate asserts allocations, not wall-clock time
`AllocsPerRun` is deterministic across machines, so a ±10% ceiling does not flake under `AllocsPerRun` is deterministic across machines, so a 10% ceiling does not flake under
CI load, while time varies with the machine and its neighbours. A rendering slowdown CI load, while time varies with the machine and its neighbours. A rendering slowdown
almost always costs an allocation too (a lost pre-size, a per-item map, an extra copy). almost always costs an allocation too (a lost pre-size, a per-item map, an extra copy).
The benchmark suite (the README's Development) reports time for a human, not as a pass/fail gate. The benchmark suite (the README's Development) reports time for a human, not as a pass/fail gate.
@@ -689,8 +689,8 @@ though `hasRead` states the same stop rule. Valid while the two differ in where
2026-09-28, larv-review on systems-architect's finding; awaiting approval. Goals 2 and 2026-09-28, larv-review on systems-architect's finding; awaiting approval. Goals 2 and
11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` each loop over `stepInto` and 11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` each loop over `stepInto` and
`table.route` themselves, and `descend` builds its probe in place, so the probe's `table.route` themselves, and `descend` builds its probe in place, so the probe's
`drawn` map stays on the stack; its caller always builds that map, since no walker `drawn` map stays on the stack; its caller always builds that map, so `readRow` never
picks a behaviour by a nil field. One walk calling them through an interface or a type tests it for nil. One walk calling them through an interface or a type
parameter leaks the walker and the pins it holds, an allocation per reference read. parameter leaks the walker and the pins it holds, an allocation per reference read.
Valid while Go's escape analysis treats a call through an interface or a type parameter Valid while Go's escape analysis treats a call through an interface or a type parameter
as leaking its receiver. as leaking its receiver.
+2 -2
View File
@@ -24,8 +24,8 @@ type tableSel struct {
spelling string spelling string
} }
// tableReadOf replays a reference's selectors through the walk a render uses, so // tableReadOf replays a reference path through pathProbe, so two paths pinning one
// two paths pinning one row by different routes compare equal. checkPath proved // row by different routes compare equal. checkPath proved
// each selector names a row. // each selector names a row.
func tableReadOf(head node, a arm, leaf node) *tableRead { func tableReadOf(head node, a arm, leaf node) *tableRead {
t, isTable := head.(*table) t, isTable := head.(*table)
+1 -2
View File
@@ -25,8 +25,7 @@ type draw struct {
// token addresses by dotted path, or a field an operand reads — is drawn once and // token addresses by dotted path, or a field an operand reads — is drawn once and
// kept, so {place.postal-code} and {place.locality} read one row, either read twice // kept, so {place.postal-code} and {place.locality} read one row, either read twice
// gives one value, and a shown operand is the operand computed. Every other name is // gives one value, and a shown operand is the operand computed. Every other name is
// drawn afresh, so {word} {word} still draws twice. checkTokens, checkPath and // drawn afresh, so {word} {word} still draws twice.
// linkRefs prove every step, so the walk cannot fail.
func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw { func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw {
if s.trace != nil { if s.trace != nil {
traceRead(s.trace, t, sc, a) traceRead(s.trace, t, sc, a)
+2 -2
View File
@@ -97,8 +97,8 @@ func operandReader(t *template, head string) string {
return "" return ""
} }
// coverPath collects what holding one path pins: every choice level the path // coverPath collects what holding one path pins: the first choice level it passes,
// passes through, whole, and the leaf it renders. // whole, else the leaf it renders; it reads no row.
func coverPath(n node, tail []string, into map[node]bool) { func coverPath(n node, tail []string, into map[node]bool) {
_, _ = (&pathCover{into: into}).walk(n, tail) _, _ = (&pathCover{into: into}).walk(n, tail)
} }
+1 -1
View File
@@ -23,7 +23,7 @@ func (*folder) isNode() {}
// choice picks one of its items. cum holds cumulative weights for a weighted // choice picks one of its items. cum holds cumulative weights for a weighted
// pick; when nil the choice is uniform and selection is O(1). shared is the set of // pick; when nil the choice is uniform and selection is O(1). shared is the set of
// relative dot paths every item can address, so descend and List both read the one // relative dot paths every item can address, so carriedByAll and List both read the one
// answer to what a path may reach through this choice. // answer to what a path may reach through this choice.
type choice struct { type choice struct {
items []node items []node
+10 -14
View File
@@ -125,8 +125,7 @@ func nameSegments(segs []string) []string {
return out return out
} }
// stepInto is what seg names under n: a folder's entry or a template's field. A // stepInto is what seg names under n: a folder's entry or a template's field.
// missing segment is an error, so no walk reaches past what the data holds.
func stepInto(n node, seg string) (node, error) { func stepInto(n node, seg string) (node, error) {
if isSelector(seg) { if isSelector(seg) {
return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(seg)) return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(seg))
@@ -148,7 +147,7 @@ func stepInto(n node, seg string) (node, error) {
} }
// tableRoute is how a path passes one table: the row it reads, by selector or // tableRoute is how a path passes one table: the row it reads, by selector or
// drawn, and what it goes on into, a linked table it descends to among them. // drawn, what it goes on into, and whether that is a linked table.
type tableRoute struct { type tableRoute struct {
sel string sel string
draw bool draw bool
@@ -188,7 +187,7 @@ func (t *table) route(tail []string, descended bool) (tableRoute, error) {
// pathCheck proves a path resolves whichever way the draws go: every variant of a // pathCheck proves a path resolves whichever way the draws go: every variant of a
// choice carries the rest of it, and is walked, a selector names a row inside the // choice carries the rest of it, and is walked, a selector names a row inside the
// rows selected before it, and no level read carries a repeat or a drawGroup. It // rows selected before it, and no level read carries a repeat or a drawGroup. It
// collects every leaf the path may render; level and tail name a refused level. // collects every leaf the path may render; level names the head in its errors.
type pathCheck struct { type pathCheck struct {
pins pinSet pins pinSet
level string level string
@@ -258,7 +257,6 @@ func (w *pathCheck) enter(t *template, rest []string) error {
return nil return nil
} }
// pathCover is coverPath's walk: it stops at a choice and reads no row.
type pathCover struct{ into map[node]bool } type pathCover struct{ into map[node]bool }
func (w *pathCover) walk(n node, tail []string) (node, error) { func (w *pathCover) walk(n node, tail []string) (node, error) {
@@ -285,9 +283,8 @@ func (w *pathCover) walk(n node, tail []string) (node, error) {
return n, nil return n, nil
} }
// pathProbe proves a path resolves, walking one variant of a choice, so a // pathProbe proves a path resolves without drawing: carriedByAll lets one variant
// path that resolves resolves whichever variants and rows are drawn. It draws // of a choice stand for all. It marks in drawn the tables a draw would pin.
// nothing, marking in drawn the tables a draw would pin.
type pathProbe struct { type pathProbe struct {
pins pinSet pins pinSet
drawn map[*table]bool drawn map[*table]bool
@@ -345,8 +342,8 @@ type pathDraw struct {
a *arm a *arm
} }
// drawPath walks w over a path proved first, by a probe or by New's fences, so the // drawPath walks w over a path proved first, by a probe or at load, so the walk
// walk cannot fail. head is what n is reached by, for the panic. // cannot fail. head names n in the panic.
func drawPath(n node, tail []string, head string, w *pathDraw) node { func drawPath(n node, tail []string, head string, w *pathDraw) node {
leaf, err := w.walk(n, tail) leaf, err := w.walk(n, tail)
if err != nil { if err != nil {
@@ -440,8 +437,7 @@ func carriedByAll(c *choice, rest []string) error {
} }
// unreachableInChoice reports that a path cannot step through this choice, listing // unreachableInChoice reports that a path cannot step through this choice, listing
// what every variant does carry. It reads the precomputed set, so a failing path // what every variant does carry.
// costs no more than a rendering one.
func unreachableInChoice(c *choice, want string) error { func unreachableInChoice(c *choice, want string) error {
if len(c.shared) == 0 { if len(c.shared) == 0 {
return fmt.Errorf("no variant of this %d-way choice carries %q", len(c.items), want) return fmt.Errorf("no variant of this %d-way choice carries %q", len(c.items), want)
@@ -454,8 +450,8 @@ func unreachableInChoice(c *choice, want string) error {
return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered) return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered)
} }
// checkPath proves a dotted tail as pathCheck does, so a path that validates here // checkPath runs pathCheck over a path read at load; level names the head in its
// resolves on every render, and a typo is a New-time error. // errors.
func checkPath(n node, tail []string, level string) error { func checkPath(n node, tail []string, level string) error {
_, err := (&pathCheck{level: level, tail: tail}).run(n) _, err := (&pathCheck{level: level, tail: tail}).run(n)
return err return err
+3 -7
View File
@@ -36,14 +36,10 @@ func (f *Generator) Fake(path string) (string, error) {
return render(f.rand, n, sc), nil return render(f.rand, n, sc), nil
} }
// descend walks named fields to the node a path names, pinning the table rows it // descend walks a caller's path to the node it names, pinning in sc the rows it
// selects or draws in sc. It is the one render-side step that can fail, because the // selects or draws.
// path comes from the caller and may name a field or a row that does not exist. A
// choice consumes no segment, so the rest of the path must be one every variant
// carries before a variant is picked — a path that resolves at all resolves on
// every call.
func descend(s *session, root node, segments []string, sc renderScope) (node, error) { func descend(s *session, root node, segments []string, sc renderScope) (node, error) {
// Walked once without drawing first, so a path that fails moves no seeded stream. // docs/decisions.md#a-path-is-walked-once-without-drawing-before-it-is-walked-for-real
if _, err := (&pathProbe{drawn: map[*table]bool{}}).walk(root, segments); err != nil { if _, err := (&pathProbe{drawn: map[*table]bool{}}).walk(root, segments); err != nil {
return nil, err return nil, err
} }