Run New, NewTemplate and FakeStruct through one bind pipeline naming where they differ
This commit is contained in:
@@ -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
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -62,17 +62,7 @@ func loadData(sources []dataSource) (map[string]node, error) {
|
||||
if len(root) == 0 {
|
||||
return nil, fmt.Errorf("no .json data found")
|
||||
}
|
||||
setTablePaths(root)
|
||||
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 {
|
||||
if err := treeBinding(root).bind(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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) }
|
||||
}
|
||||
|
||||
func checkScope(s nodeScope) error {
|
||||
if err := checkColumns(s); err != nil {
|
||||
// binding is one entry point's nodes on their way through bind, the load pipeline New,
|
||||
// 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 checkRenders(s)
|
||||
return linkRefs(root)
|
||||
},
|
||||
noCycles: func() error { return checkNoCycles(root) },
|
||||
}
|
||||
}
|
||||
|
||||
func (b binding) bind() error {
|
||||
if err := b.link(); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
|
||||
+1
-1
@@ -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
|
||||
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.
|
||||
|
||||
## An inline template that does not compile is misuse (exit 2), including a reference that resolves to nothing
|
||||
|
||||
@@ -32,7 +32,7 @@ func (f *Generator) NewTemplate(input string) (*Template, error) {
|
||||
if err != nil {
|
||||
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 &Template{g: f, n: n}, nil
|
||||
@@ -146,17 +146,17 @@ func inputValue(input string) (any, error) {
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// bindInline links an inline node's references against root and runs check over it, naming its
|
||||
// nodes from label.
|
||||
func bindInline(n node, label string, root map[string]node, check func(nodeScope) error) error {
|
||||
// inlineBinding binds an inline node against root, naming its nodes from label.
|
||||
func inlineBinding(n node, label string, root map[string]node) binding {
|
||||
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 != "" {
|
||||
return fmt.Errorf("%s: drawGroup %q names nothing, since nothing can reference an inline template; drop it", label, t.drawGroup)
|
||||
}
|
||||
scope := inlineScope(n, label)
|
||||
if err := linkNodeRefs(scope, root); err != nil {
|
||||
return err
|
||||
}
|
||||
return check(scope)
|
||||
return linkNodeRefs(scope, root)
|
||||
}
|
||||
|
||||
// linkNodeRefs binds the references in an inline node's templates against the
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ func TestEveryFormatCompilesAtLink(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
for format, has := range compiledByPath() {
|
||||
|
||||
@@ -258,7 +258,9 @@ func (s *structShape) compileRecord(categories map[string]node, t reflect.Type,
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
record, columns, err := recordOf(n)
|
||||
|
||||
@@ -21,8 +21,6 @@ Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
|
||||
|
||||
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
|
||||
runs a proof to pick an error's wording.
|
||||
- Derive a column's datatype in one place: `columnItems` yields nothing for a
|
||||
|
||||
Reference in New Issue
Block a user