Walk the own-family fence through a repeat and a draw group, and record that tables with token cells stay small
This commit was merged in pull request #19.
This commit is contained in:
@@ -444,9 +444,10 @@ table another path in the group selects a row of, `{/city.name}` beside
|
|||||||
`{/country[SE].name}`, which the first token rendered would otherwise decide. `New`
|
`{/country[SE].name}`, which the first token rendered would otherwise decide. `New`
|
||||||
also refuses a link cell that is no key of the parent, a parent row no child links
|
also refuses a link cell that is no key of the parent, a parent row no child links
|
||||||
to, a chain of parents that closes, a table named like a column of any table above
|
to, a chain of parents that closes, a table named like a column of any table above
|
||||||
it, and a cell or format of a table that references a table of its own family, since
|
it, and a cell or format of a table that references a table of its own family, through
|
||||||
a row rendered whole would draw the family apart from itself: read the family from a
|
any template, a `repeat` or a `drawGroup` included, since a row rendered whole would
|
||||||
template beside it, or add the value as a column.
|
draw the family apart from itself: read the family from a template beside it, or add
|
||||||
|
the value as a column.
|
||||||
|
|
||||||
### Options and fields
|
### Options and fields
|
||||||
|
|
||||||
@@ -969,6 +970,16 @@ renamed or retyped line is a major.
|
|||||||
they sit in two rows of one table, or in a row outside a selected ancestor's. A
|
they sit in two rows of one table, or in a row outside a selected ancestor's. A
|
||||||
choice's items get no such treatment yet: two items selecting different rows
|
choice's items get no such treatment yet: two items selecting different rows
|
||||||
are still refused.
|
are still refused.
|
||||||
|
- **A table never reaches its own family, by any route.** A `repeat` iteration and a
|
||||||
|
`drawGroup` each draw apart on purpose, but a row that lists three localities from
|
||||||
|
other regions is the output the family exists to prevent, so the own-family fence
|
||||||
|
walks through both rather than stopping where the draw fences do.
|
||||||
|
- **Tables carrying token cells stay small.** The family fence compares the reads of
|
||||||
|
every pair of rows that can render together, so a table whose every row's cell
|
||||||
|
selects a row of another table loads in time quadratic in its rows: about a
|
||||||
|
second at four thousand rows. No shipped table carries such cells, and a register
|
||||||
|
is a column set rather than a set of references, so the fence is left as it is
|
||||||
|
until a real data set needs the indexed form.
|
||||||
- **A path is walked once without drawing before it is walked for real.** A path
|
- **A path is walked once without drawing before it is walked for real.** A path
|
||||||
that fails below its first level then moves no seeded stream, at the cost of one
|
that fails below its first level then moves no seeded stream, at the cost of one
|
||||||
draw-free walk per call, which allocates nothing.
|
draw-free walk per call, which allocates nothing.
|
||||||
|
|||||||
@@ -137,16 +137,19 @@ func (c *drawCheck) checkDrawGroup(path string, n node) error {
|
|||||||
|
|
||||||
func (c *drawCheck) checkDraws(path string, n node) error {
|
func (c *drawCheck) checkDraws(path string, n node) error {
|
||||||
t, ok := n.(*template)
|
t, ok := n.(*template)
|
||||||
if !ok || !c.readsPath(t) {
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := checkOwnFamily(t); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", path, err)
|
||||||
|
}
|
||||||
|
if !c.readsPath(t) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
w := newDrawWalk()
|
w := newDrawWalk()
|
||||||
for _, e := range renderEdges(t) {
|
for _, e := range renderEdges(t) {
|
||||||
w.edge(t, e, drawAt{group: t.drawGroupKey, route: drawRoute{e.reached(), e.label}})
|
w.edge(t, e, drawAt{group: t.drawGroupKey, route: drawRoute{e.reached(), e.label}})
|
||||||
}
|
}
|
||||||
if err := w.checkOwnFamily(t); err != nil {
|
|
||||||
return fmt.Errorf("%s: %w", path, err)
|
|
||||||
}
|
|
||||||
if err := w.check(); err != nil {
|
if err := w.check(); err != nil {
|
||||||
return fmt.Errorf("%s: %w", path, err)
|
return fmt.Errorf("%s: %w", path, err)
|
||||||
}
|
}
|
||||||
@@ -154,9 +157,10 @@ func (c *drawCheck) checkDraws(path string, n node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkOwnFamily refuses a table's format or cell that reads, however many templates
|
// checkOwnFamily refuses a table's format or cell that reads, however many templates
|
||||||
// away, a table of its own family: a row rendered whole draws its row without
|
// away and through a repeat or a draw group too, a table of its own family: a row
|
||||||
// pinning it, so the family would draw apart from the row being rendered.
|
// rendered whole draws its row without pinning it, so the family would draw apart
|
||||||
func (w *drawWalk) checkOwnFamily(t *template) error {
|
// from the row being rendered, whichever draws the reaching template holds.
|
||||||
|
func checkOwnFamily(t *template) error {
|
||||||
own := t.table
|
own := t.table
|
||||||
if own == nil {
|
if own == nil {
|
||||||
own = t.cellOf
|
own = t.cellOf
|
||||||
@@ -164,10 +168,27 @@ func (w *drawWalk) checkOwnFamily(t *template) error {
|
|||||||
if own == nil {
|
if own == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, r := range w.reads {
|
seen := map[node]bool{}
|
||||||
if r.tr != nil && r.tr.head.family() == own.family() {
|
var find func(n node) (renderEdge, *table, bool)
|
||||||
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", r.at.route.spelled(r.a.name), r.tr.head.category, own.category)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user