Identify a fence alternative by the table row rather than the cell, keep the outermost row through a reached table, and key reads and visits by it
This commit is contained in:
@@ -962,12 +962,15 @@ renamed or retyped line is a major.
|
|||||||
format and options, at the cost of holding the rows twice, which is what a
|
format and options, at the cost of holding the rows twice, which is what a
|
||||||
category over a register with two natural formats asks for; a TSV nothing names
|
category over a register with two natural formats asks for; a TSV nothing names
|
||||||
stays a load error, since that one is a file forgotten rather than shared.
|
stays a load error, since that one is a file forgotten rather than shared.
|
||||||
- **The cells of one column are alternatives.** Only one row renders, so two cells
|
- **The rows of a table are alternatives.** Only one row renders, so a cell in one
|
||||||
selecting different rows of another table never meet, as two items of a choice
|
row and a cell in another never meet, and each may select its own row of another
|
||||||
never do; the family fence replays the reads of each cell apart, together with
|
table; the cells of one row, and whatever they reach, do meet, and so does the
|
||||||
the reads outside any cell. A path is walked once without drawing before it is
|
format beside them. The family fence replays the reads of each row apart,
|
||||||
walked for real, so a path that fails below its first level moves no seeded
|
together with the reads outside any row. A choice's items get no such treatment
|
||||||
stream.
|
yet: two items selecting different rows are still refused.
|
||||||
|
- **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
|
||||||
|
draw-free walk per call, which allocates nothing.
|
||||||
- **`List` advertises direct descents only.** `region.municipality.locality` is
|
- **`List` advertises direct descents only.** `region.municipality.locality` is
|
||||||
listed, and `region.locality` resolves too but is not: the set of every descent
|
listed, and `region.locality` resolves too but is not: the set of every descent
|
||||||
through a chain of five tables is every subsequence of it, and the direct chain is
|
through a chain of five tables is every subsequence of it, and the direct chain is
|
||||||
|
|||||||
@@ -271,17 +271,23 @@ type drawWalk struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// drawAt is where a walk stands: the draw group it draws in, how the render's root reached it, and
|
// drawAt is where a walk stands: the draw group it draws in, how the render's root reached it, and
|
||||||
// the table cell it entered, if any: the cells of one column are alternatives, as a choice's items
|
// the table row it entered, if any.
|
||||||
// are, so reads in two of them never meet.
|
|
||||||
type drawAt struct {
|
type drawAt struct {
|
||||||
group string
|
group string
|
||||||
route drawRoute
|
route drawRoute
|
||||||
cell node
|
alt rowAlt
|
||||||
}
|
}
|
||||||
|
|
||||||
// alternatives reports whether two reads sit in different cells of one column.
|
// rowAlt is one row of a table as an alternative: only one row renders, so reads in two rows of
|
||||||
|
// one table never meet, while reads in one row, across its columns and whatever they reach, do.
|
||||||
|
type rowAlt struct {
|
||||||
|
t *table
|
||||||
|
row int
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternatives reports whether two reads sit in different rows of one table.
|
||||||
func alternatives(a, b drawAt) bool {
|
func alternatives(a, b drawAt) bool {
|
||||||
return a.cell != nil && b.cell != nil && a.cell != b.cell
|
return a.alt.t != nil && a.alt.t == b.alt.t && a.alt.row != b.alt.row
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -297,11 +303,13 @@ type pathRead struct {
|
|||||||
type drawVisit struct {
|
type drawVisit struct {
|
||||||
n node
|
n node
|
||||||
group string
|
group string
|
||||||
|
alt rowAlt
|
||||||
}
|
}
|
||||||
|
|
||||||
type drawKey struct {
|
type drawKey struct {
|
||||||
group string
|
group string
|
||||||
path string
|
path string
|
||||||
|
alt rowAlt
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDrawWalk() *drawWalk {
|
func newDrawWalk() *drawWalk {
|
||||||
@@ -311,7 +319,7 @@ func newDrawWalk() *drawWalk {
|
|||||||
// walk follows what rendering n renders. A repeat renders over draws of its own, so the walk stops
|
// walk follows what rendering n renders. A repeat renders over draws of its own, so the walk stops
|
||||||
// there.
|
// there.
|
||||||
func (w *drawWalk) walk(n node, at drawAt) {
|
func (w *drawWalk) walk(n node, at drawAt) {
|
||||||
v := drawVisit{n, at.group}
|
v := drawVisit{n, at.group, at.alt}
|
||||||
if w.seen[v] {
|
if w.seen[v] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -324,16 +332,18 @@ func (w *drawWalk) walk(n node, at drawAt) {
|
|||||||
}
|
}
|
||||||
_, isColumn := n.(*column)
|
_, isColumn := n.(*column)
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
if isColumn {
|
// The outermost row is kept: a cell reached through another row's cell renders with it.
|
||||||
at.cell = e.to
|
edgeAt := at
|
||||||
|
if cell, _ := e.to.(*template); isColumn && at.alt.t == nil {
|
||||||
|
edgeAt.alt = rowAlt{cell.cellOf, cell.cellRow}
|
||||||
}
|
}
|
||||||
w.edge(n, e, at)
|
w.edge(n, e, edgeAt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
||||||
if a, reads := refRead(from, e.label); reads {
|
if a, reads := refRead(from, e.label); reads {
|
||||||
if k := (drawKey{at.group, a.path}); !w.read[k] {
|
if k := (drawKey{at.group, a.path, at.alt}); !w.read[k] {
|
||||||
w.read[k] = true
|
w.read[k] = true
|
||||||
w.reads = append(w.reads, pathRead{at, a, tableReadOf(from.(*template).fields[a.key], a, e.to)})
|
w.reads = append(w.reads, pathRead{at, a, tableReadOf(from.(*template).fields[a.key], a, e.to)})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,28 +243,39 @@ func checkFamilies(reads []pathRead) error {
|
|||||||
return replayAlternatives(reads)
|
return replayAlternatives(reads)
|
||||||
}
|
}
|
||||||
|
|
||||||
// replayAlternatives replays the reads of each draw group into one draws per cell
|
// replayAlternatives replays the reads of each draw group into one draws per row
|
||||||
// alternative — the reads outside any cell, then the cell's own — so two cells of
|
// alternative — the reads outside any row, then the row's own — so two rows of one
|
||||||
// one column are never replayed together.
|
// table are never replayed together.
|
||||||
func replayAlternatives(reads []pathRead) error {
|
func replayAlternatives(reads []pathRead) error {
|
||||||
type alt struct {
|
type alt struct {
|
||||||
group string
|
group string
|
||||||
cell node
|
row rowAlt
|
||||||
}
|
}
|
||||||
var alts []alt
|
var alts []alt
|
||||||
seen := map[alt]bool{}
|
outside, inside := map[string][]pathRead{}, map[alt][]pathRead{}
|
||||||
for _, r := range reads {
|
for _, r := range reads {
|
||||||
if a := (alt{r.at.group, r.at.cell}); r.tr != nil && !seen[a] {
|
if r.tr == nil {
|
||||||
seen[a] = true
|
|
||||||
alts = append(alts, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, a := range alts {
|
|
||||||
var d draws
|
|
||||||
for _, r := range reads {
|
|
||||||
if r.tr == nil || r.at.group != a.group || r.at.cell != nil && r.at.cell != a.cell {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if r.at.alt.t == nil {
|
||||||
|
outside[r.at.group] = append(outside[r.at.group], r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
a := alt{r.at.group, r.at.alt}
|
||||||
|
if inside[a] == nil {
|
||||||
|
alts = append(alts, a)
|
||||||
|
}
|
||||||
|
inside[a] = append(inside[a], r)
|
||||||
|
}
|
||||||
|
for group := range outside {
|
||||||
|
alts = append(alts, alt{group, rowAlt{}})
|
||||||
|
}
|
||||||
|
sort.Slice(alts, func(i, j int) bool {
|
||||||
|
return altOrder(alts[i].group, alts[i].row) < altOrder(alts[j].group, alts[j].row)
|
||||||
|
})
|
||||||
|
for _, a := range alts {
|
||||||
|
var d draws
|
||||||
|
for _, r := range append(append([]pathRead(nil), outside[a.group]...), inside[a]...) {
|
||||||
if err := r.tr.replay(&d); err != nil {
|
if err := r.tr.replay(&d); err != nil {
|
||||||
return fmt.Errorf("%s: %w; select the same rows in every path into the family, or draw them apart with a drawGroup", r.at.route.spelled(r.a.name), err)
|
return fmt.Errorf("%s: %w; select the same rows in every path into the family, or draw them apart with a drawGroup", r.at.route.spelled(r.a.name), err)
|
||||||
}
|
}
|
||||||
@@ -273,6 +284,15 @@ func replayAlternatives(reads []pathRead) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// altOrder sorts alternatives by group, the reads outside any row first, then by table and row,
|
||||||
|
// so which conflict is reported does not vary.
|
||||||
|
func altOrder(group string, r rowAlt) string {
|
||||||
|
if r.t == nil {
|
||||||
|
return group + "\x00"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s\x01%s\x00%08d", group, r.t.category, r.row)
|
||||||
|
}
|
||||||
|
|
||||||
// replay pins the read's rows into d, where they agree with the rows pinned before.
|
// replay pins the read's rows into d, where they agree with the rows pinned before.
|
||||||
func (r *tableRead) replay(d *draws) error {
|
func (r *tableRead) replay(d *draws) error {
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ type template struct {
|
|||||||
record bool // compiled at the top without a repeat, so its fields are record columns
|
record bool // compiled at the top without a repeat, so its fields are record columns
|
||||||
table *table // the table whose format this is, whose columns are the fields
|
table *table // the table whose format this is, whose columns are the fields
|
||||||
cellOf *table // the table whose cell this is
|
cellOf *table // the table whose cell this is
|
||||||
|
cellRow int // the row the cell sits in
|
||||||
drawGroup string // the draw group it draws in, as written; "" keeps its caller's
|
drawGroup string // the draw group it draws in, as written; "" keeps its caller's
|
||||||
drawGroupKey string // its draw group keyed by its category once linked: what a render reads its reference paths under
|
drawGroupKey string // its draw group keyed by its category once linked: what a render reads its reference paths under
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ func (t *table) checkCells() error {
|
|||||||
t.tokens = map[int]*template{}
|
t.tokens = map[int]*template{}
|
||||||
}
|
}
|
||||||
t.tokens[i] = n.(*template)
|
t.tokens[i] = n.(*template)
|
||||||
t.tokens[i].cellOf = t
|
t.tokens[i].cellOf, t.tokens[i].cellRow = t, row
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user