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
7 changed files with 58 additions and 29 deletions
Showing only changes of commit cfe653de4d - Show all commits
+1 -1
View File
@@ -890,7 +890,7 @@ transform.go the builtins that rewrite an operand's value, and the ASCII fold
calc.go the {calc()} arithmetic evaluator: parser, eval, validation calc.go the {calc()} arithmetic evaluator: parser, eval, validation
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
value.go the value proof: what a typed column or calc operand holds, checked at load value.go the value proof: what a typed column or calc operand holds, checked at load
data.go the load path: fs.FS folders/files -> merged namespace tree, and the fence order a scope runs data.go the load path: fs.FS folders/files -> merged namespace tree, and the bind pipeline every entry point runs
cmd/fejkdata/ the fejkdata CLI cmd/fejkdata/ the fejkdata CLI
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md) data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md)
+43 -14
View File
@@ -62,17 +62,7 @@ func loadData(sources []dataSource) (map[string]node, error) {
if len(root) == 0 { if len(root) == 0 {
return nil, fmt.Errorf("no .json data found") return nil, fmt.Errorf("no .json data found")
} }
setTablePaths(root) if err := treeBinding(root).bind(); err != nil {
if err := linkTables(root); err != nil {
return nil, err
}
if err := linkRefs(root); err != nil {
return nil, err
}
if err := checkNoCycles(root); err != nil {
return nil, err
}
if err := checkScope(treeScope(root)); err != nil {
return nil, err return nil, err
} }
return root, nil return root, nil
@@ -206,11 +196,50 @@ func inlineScope(n node, label string) nodeScope {
return func(fn func(path string, m node) error) error { return eachNode(n, label, fn) } return func(fn func(path string, m node) error) error { return eachNode(n, label, fn) }
} }
func checkScope(s nodeScope) error { // binding is one entry point's nodes on their way through bind, the load pipeline New,
if err := checkColumns(s); err != nil { // NewTemplate and FakeStruct share; its fields are where they differ.
type binding struct {
scope nodeScope
// link binds the references: a loaded tree's relative to each category's folder, an
// inline node's from the root alone.
link func() error
// noCycles refuses a reference cycle, which only a loaded tree can close: nothing
// references an inline node.
noCycles func() error
// typedByGo marks a struct's columns, whose datatypes its Go types set and checkField
// proves, so checkColumns skips them.
typedByGo bool
}
func treeBinding(root map[string]node) binding {
return binding{
scope: treeScope(root),
link: func() error {
setTablePaths(root)
if err := linkTables(root); err != nil {
return err
}
return linkRefs(root)
},
noCycles: func() error { return checkNoCycles(root) },
}
}
func (b binding) bind() error {
if err := b.link(); err != nil {
return err return err
} }
return checkRenders(s) if b.noCycles != nil {
if err := b.noCycles(); err != nil {
return err
}
}
if !b.typedByGo {
if err := checkColumns(b.scope); err != nil {
return err
}
}
return checkRenders(b.scope)
} }
// checkRenders runs the per-node fences over a scope, each over the whole scope // checkRenders runs the per-node fences over a scope, each over the whole scope
+1 -1
View File
@@ -57,7 +57,7 @@ tags and any other caller read one.
`New` proves the loaded tree acyclic, an inline node is a finite tree of its own, and `New` proves the loaded tree acyclic, an inline node is a finite tree of its own, and
nothing in the tree can reference it, so no render of it reaches itself. Every other nothing in the tree can reference it, so no render of it reaches itself. Every other
fence runs over both, from one `checkScope`, except that struct tags leave column fence runs over both, from one `binding.bind`, except that struct tags leave column
agreement to their Go types. agreement to their Go types.
## An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing ## An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing
+9 -9
View File
@@ -32,7 +32,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("fejkdata: %w", err) return nil, fmt.Errorf("fejkdata: %w", err)
} }
if err := bindInline(n, "template", f.categories, checkScope); err != nil { if err := inlineBinding(n, "template", f.categories).bind(); err != nil {
return nil, fmt.Errorf("fejkdata: %w", err) return nil, fmt.Errorf("fejkdata: %w", err)
} }
return &Template{g: f, n: n}, nil return &Template{g: f, n: n}, nil
@@ -146,17 +146,17 @@ func inputValue(input string) (any, error) {
return raw, nil return raw, nil
} }
// bindInline links an inline node's references against root and runs check over it, naming its // inlineBinding binds an inline node against root, naming its nodes from label.
// nodes from label. func inlineBinding(n node, label string, root map[string]node) binding {
func bindInline(n node, label string, root map[string]node, check func(nodeScope) error) error { scope := inlineScope(n, label)
return binding{scope: scope, link: func() error { return linkInline(n, label, scope, root) }}
}
func linkInline(n node, label string, scope nodeScope, root map[string]node) error {
if t, isTemplate := n.(*template); isTemplate && t.drawGroup != "" { if t, isTemplate := n.(*template); isTemplate && t.drawGroup != "" {
return fmt.Errorf("%s: drawGroup %q names nothing, since nothing can reference an inline template; drop it", label, t.drawGroup) return fmt.Errorf("%s: drawGroup %q names nothing, since nothing can reference an inline template; drop it", label, t.drawGroup)
} }
scope := inlineScope(n, label) return linkNodeRefs(scope, root)
if err := linkNodeRefs(scope, root); err != nil {
return err
}
return check(scope)
} }
// linkNodeRefs binds the references in an inline node's templates against the // linkNodeRefs binds the references in an inline node's templates against the
+1 -1
View File
@@ -107,7 +107,7 @@ func TestEveryFormatCompilesAtLink(t *testing.T) {
t.Errorf("%s compiled before link", format) t.Errorf("%s compiled before link", format)
} }
} }
if err := bindInline(n, "t", map[string]node{"w": w}, func(nodeScope) error { return nil }); err != nil { if err := linkInline(n, "t", inlineScope(n, "t"), map[string]node{"w": w}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for format, has := range compiledByPath() { for format, has := range compiledByPath() {
+3 -1
View File
@@ -258,7 +258,9 @@ func (s *structShape) compileRecord(categories map[string]node, t reflect.Type,
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", label, err) return fmt.Errorf("%s: %w", label, err)
} }
if err := bindInline(n, label, categories, checkRenders); err != nil { b := inlineBinding(n, label, categories)
b.typedByGo = true
if err := b.bind(); err != nil {
return err return err
} }
record, columns, err := recordOf(n) record, columns, err := recordOf(n)
-2
View File
@@ -21,8 +21,6 @@ Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
This round, in order: This round, in order:
- Run `New`, `NewTemplate` and `FakeStruct` through one bind pipeline that names
where they differ: each picks its own fences today.
- Make `disagreement` and `bothTyped` a table of cases: each swaps its operands and - Make `disagreement` and `bothTyped` a table of cases: each swaps its operands and
runs a proof to pick an error's wording. runs a proof to pick an error's wording.
- Derive a column's datatype in one place: `columnItems` yields nothing for a - Derive a column's datatype in one place: `columnItems` yields nothing for a