Gather the family fence in family.go
This commit is contained in:
@@ -1341,8 +1341,8 @@ template.go the {token} grammar: scanning, tokens, operands, validation, com
|
|||||||
hold.go the hold: one draw per expansion for paths and operands, and the rows it pins
|
hold.go the hold: one draw per expansion for paths and operands, and the rows it pins
|
||||||
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
|
||||||
holdset.go one reference draw per render and group: the hold set a render reads its paths through
|
holdset.go one reference draw per render and group: the hold set a render reads its paths through
|
||||||
drawfence.go the load fences over reference draws: the drawGroup option, and the walk over a render's reads
|
drawfence.go the load fences over reference draws: the walk over a render's reads
|
||||||
family.go a family of linked tables: what a reference path reads of one, and the fence over paths into it
|
family.go a family of linked tables: what a reference path reads of one, the rows a walk enters, and the fences over paths into it
|
||||||
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
|
||||||
builtins.go the {name()} function registry and its implementations
|
builtins.go the {name()} function registry and its implementations
|
||||||
|
|||||||
-131
@@ -6,33 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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) {
|
|
||||||
v, ok := m["drawGroup"]
|
|
||||||
if !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
name, ok := v.(string)
|
|
||||||
switch {
|
|
||||||
case !ok:
|
|
||||||
return "", fmt.Errorf("drawGroup must be a string, got %T", v)
|
|
||||||
case name == "":
|
|
||||||
return "", fmt.Errorf(`drawGroup "" is the default, so it has no effect; drop it`)
|
|
||||||
case repeat > 1:
|
|
||||||
return "", fmt.Errorf("drawGroup %q on a repeat names nothing, since each iteration is a render of its own; drop it", name)
|
|
||||||
}
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyDrawGroup keys t's draw group by the category t sits in, "" for an inline template, so a name
|
|
||||||
// is local to its category.
|
|
||||||
func (t *template) keyDrawGroup(category string) {
|
|
||||||
if t.drawGroup != "" {
|
|
||||||
t.drawGroupKey = category + "/" + t.drawGroup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkNestedDrawGroup refuses a template beneath one drawing in group that names group again,
|
// checkNestedDrawGroup refuses a template beneath one drawing in group that names group again,
|
||||||
// short of a repeat or another draw group.
|
// short of a repeat or another draw group.
|
||||||
func checkNestedDrawGroup(fields map[string]node, group string) error {
|
func checkNestedDrawGroup(fields map[string]node, group string) error {
|
||||||
@@ -104,43 +77,6 @@ func (c *drawCheck) 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 row
|
|
||||||
// rendered whole draws its row without pinning it, so the family would draw apart
|
|
||||||
// from the row being rendered, whichever draws the reaching template holds.
|
|
||||||
func checkOwnFamily(t *template) error {
|
|
||||||
own := t.table
|
|
||||||
if own == nil {
|
|
||||||
own = t.cellOf
|
|
||||||
}
|
|
||||||
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 a, isRef := refRead(n, e.label); isRef {
|
|
||||||
if head, isTable := n.(*template).fields[a.key].(*table); isTable && head.family() == own.family() {
|
|
||||||
return e, head, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e, head, found := find(e.to); found {
|
|
||||||
return e, head, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return renderEdge{}, nil, false
|
|
||||||
}
|
|
||||||
if e, head, found := find(t); found {
|
|
||||||
return fmt.Errorf("%s reads %s, a table of its own family, which a row of %s rendered whole would draw apart from; read the family from a template beside it, or add the value as a column", e.reached(), head.category, own.category)
|
|
||||||
}
|
|
||||||
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.
|
||||||
func (c *drawCheck) checkRecordDraws(path string, n node) error {
|
func (c *drawCheck) checkRecordDraws(path string, n node) error {
|
||||||
@@ -248,59 +184,6 @@ type drawAt struct {
|
|||||||
drawn *table // left out of the visit keys: only this table's own cells compare against it, which the own-family fence keeps true
|
drawn *table // left out of the visit keys: only this table's own cells compare against it, which the own-family fence keeps true
|
||||||
}
|
}
|
||||||
|
|
||||||
// rowSet stands in at load for the rows (*hold).pin holds at render: a path read enters a row with
|
|
||||||
// its ancestors, a whole read enters the drawn row alone.
|
|
||||||
type rowSet []tablePin
|
|
||||||
|
|
||||||
func (s rowSet) rowOf(t *table) (int, bool) {
|
|
||||||
for _, p := range s {
|
|
||||||
if p.t == t {
|
|
||||||
return p.row, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// enter is s with row r of t, and with t's ancestors where the render pins that row rather than
|
|
||||||
// drawing it; the set is copied, since walks branch.
|
|
||||||
func (s rowSet) enter(t *table, r int, pins bool) rowSet {
|
|
||||||
if _, in := s.rowOf(t); in {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
out := append(make(rowSet, 0, len(s)+1), s...)
|
|
||||||
for {
|
|
||||||
out = append(out, tablePin{t, r})
|
|
||||||
if !pins || t.parentT == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t, r = t.parentT, t.parentRow(r)
|
|
||||||
if _, in := out.rowOf(t); in {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Slice(out, func(i, j int) bool { return out[i].t.path < out[j].t.path })
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// key spells the set for a map, by the tables' identities.
|
|
||||||
func (s rowSet) key() string {
|
|
||||||
var b strings.Builder
|
|
||||||
for _, p := range s {
|
|
||||||
fmt.Fprintf(&b, "%p[%d]", p.t, p.row)
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// alternatives reports whether two reads sit in different rows of one table.
|
|
||||||
func alternatives(a, b drawAt) bool {
|
|
||||||
for _, p := range a.alt {
|
|
||||||
if r, in := b.alt.rowOf(p.t); in && r != p.row {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// drawRoute is how a render reaches a draw: as its author spells it, and the root edge's label.
|
// drawRoute is how a render reaches a draw: as its author spells it, and the root edge's label.
|
||||||
type drawRoute struct{ spelling, label string }
|
type drawRoute struct{ spelling, label string }
|
||||||
|
|
||||||
@@ -358,20 +241,6 @@ func (w *drawWalk) walk(n node, at drawAt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// excludes reports whether a cell's row cannot render with the rows entered: another
|
|
||||||
// row of its table, or a row outside an entered ancestor's.
|
|
||||||
func (s rowSet) excludes(cell *template) bool {
|
|
||||||
for _, p := range s {
|
|
||||||
if p.t == cell.cellOf {
|
|
||||||
return p.row != cell.cellRow
|
|
||||||
}
|
|
||||||
if cell.cellOf.descends(p.t) && !cell.cellOf.under(cell.cellRow, p.t, p.row) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// edge records the reference an edge reads, then walks on with every row the read
|
// edge records the reference an edge reads, then walks on with every row the read
|
||||||
// pins entered, so a selected row renders only its own cells.
|
// pins entered, so a selected row renders only its own cells.
|
||||||
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package fejkdata
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -187,3 +188,107 @@ func (r *tableRead) drawnOf(pins *hold) *table {
|
|||||||
})
|
})
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 row
|
||||||
|
// rendered whole draws its row without pinning it, so the family would draw apart
|
||||||
|
// from the row being rendered, whichever draws the reaching template holds.
|
||||||
|
func checkOwnFamily(t *template) error {
|
||||||
|
own := t.table
|
||||||
|
if own == nil {
|
||||||
|
own = t.cellOf
|
||||||
|
}
|
||||||
|
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 a, isRef := refRead(n, e.label); isRef {
|
||||||
|
if head, isTable := n.(*template).fields[a.key].(*table); isTable && head.family() == own.family() {
|
||||||
|
return e, head, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e, head, found := find(e.to); found {
|
||||||
|
return e, head, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderEdge{}, nil, false
|
||||||
|
}
|
||||||
|
if e, head, found := find(t); found {
|
||||||
|
return fmt.Errorf("%s reads %s, a table of its own family, which a row of %s rendered whole would draw apart from; read the family from a template beside it, or add the value as a column", e.reached(), head.category, own.category)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rowSet stands in at load for the rows (*hold).pin holds at render: a path read enters a row with
|
||||||
|
// its ancestors, a whole read enters the drawn row alone.
|
||||||
|
type rowSet []tablePin
|
||||||
|
|
||||||
|
func (s rowSet) rowOf(t *table) (int, bool) {
|
||||||
|
for _, p := range s {
|
||||||
|
if p.t == t {
|
||||||
|
return p.row, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// enter is s with row r of t, and with t's ancestors where the render pins that row rather than
|
||||||
|
// drawing it; the set is copied, since walks branch.
|
||||||
|
func (s rowSet) enter(t *table, r int, pins bool) rowSet {
|
||||||
|
if _, in := s.rowOf(t); in {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
out := append(make(rowSet, 0, len(s)+1), s...)
|
||||||
|
for {
|
||||||
|
out = append(out, tablePin{t, r})
|
||||||
|
if !pins || t.parentT == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t, r = t.parentT, t.parentRow(r)
|
||||||
|
if _, in := out.rowOf(t); in {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(out, func(i, j int) bool { return out[i].t.path < out[j].t.path })
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// key spells the set for a map, by the tables' identities.
|
||||||
|
func (s rowSet) key() string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, p := range s {
|
||||||
|
fmt.Fprintf(&b, "%p[%d]", p.t, p.row)
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// excludes reports whether a cell's row cannot render with the rows entered: another
|
||||||
|
// row of its table, or a row outside an entered ancestor's.
|
||||||
|
func (s rowSet) excludes(cell *template) bool {
|
||||||
|
for _, p := range s {
|
||||||
|
if p.t == cell.cellOf {
|
||||||
|
return p.row != cell.cellRow
|
||||||
|
}
|
||||||
|
if cell.cellOf.descends(p.t) && !cell.cellOf.under(cell.cellRow, p.t, p.row) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternatives reports whether two reads sit in different rows of one table.
|
||||||
|
func alternatives(a, b drawAt) bool {
|
||||||
|
for _, p := range a.alt {
|
||||||
|
if r, in := b.alt.rowOf(p.t); in && r != p.row {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -433,6 +433,33 @@ func repeatOf(m map[string]any) (int, error) {
|
|||||||
return int(r), nil
|
return int(r), 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) {
|
||||||
|
v, ok := m["drawGroup"]
|
||||||
|
if !ok {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
name, ok := v.(string)
|
||||||
|
switch {
|
||||||
|
case !ok:
|
||||||
|
return "", fmt.Errorf("drawGroup must be a string, got %T", v)
|
||||||
|
case name == "":
|
||||||
|
return "", fmt.Errorf(`drawGroup "" is the default, so it has no effect; drop it`)
|
||||||
|
case repeat > 1:
|
||||||
|
return "", fmt.Errorf("drawGroup %q on a repeat names nothing, since each iteration is a render of its own; drop it", name)
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyDrawGroup keys t's draw group by the category t sits in, "" for an inline template, so a name
|
||||||
|
// is local to its category.
|
||||||
|
func (t *template) keyDrawGroup(category string) {
|
||||||
|
if t.drawGroup != "" {
|
||||||
|
t.drawGroupKey = category + "/" + t.drawGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// weightOf reads a node's "weight" (default 1) from its raw JSON form. Only
|
// weightOf reads a node's "weight" (default 1) from its raw JSON form. Only
|
||||||
// template objects carry weight; a present one must be finite and positive.
|
// template objects carry weight; a present one must be finite and positive.
|
||||||
func weightOf(raw any) (float64, error) {
|
func weightOf(raw any) (float64, error) {
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ by nothing but tests, whose premise lives only in the README's Decisions.
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Gather the family fence in `family.go`: `alternatives`, `rowSet` and `checkOwnFamily`
|
|
||||||
beside `checkFamilies`, and the `drawGroup` option parsing out of `drawfence.go`, so
|
|
||||||
one question about a family opens one file.
|
|
||||||
- Give render and the draw fences one pin model: a pin set `hold.pin`, `hold.rowOf`
|
- Give render and the draw fences one pin model: a pin set `hold.pin`, `hold.rowOf`
|
||||||
and the fence's `rowSet` all use, so a change to how a render pins rows cannot leave
|
and the fence's `rowSet` all use, so a change to how a render pins rows cannot leave
|
||||||
the fence proving the old rule, and `hold` stops being a proof scratchpad whenever its
|
the fence proving the old rule, and `hold` stops being a proof scratchpad whenever its
|
||||||
|
|||||||
Reference in New Issue
Block a user