Add the table node, row selection and linked tables, and ship the misc registers as tables #19
@@ -271,23 +271,54 @@ 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 row it entered, if any.
|
// the table rows it entered.
|
||||||
type drawAt struct {
|
type drawAt struct {
|
||||||
group string
|
group string
|
||||||
route drawRoute
|
route drawRoute
|
||||||
alt rowAlt
|
alt rowSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// rowAlt is one row of a table as an alternative: only one row renders, so reads in two rows of
|
// rowSet is the rows a walk entered, one per table: only one row of a table renders, so reads in
|
||||||
// one table never meet, while reads in one row, across its columns and whatever they reach, do.
|
// two rows of one table never meet, while reads in one row, across its columns and whatever they
|
||||||
type rowAlt struct {
|
// reach, do.
|
||||||
t *table
|
type rowSet []tablePin
|
||||||
row int
|
|
||||||
|
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, where t is not in it yet; the set is copied, since walks branch.
|
||||||
|
func (s rowSet) enter(t *table, r int) rowSet {
|
||||||
|
if _, in := s.rowOf(t); in {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
out := append(append(make(rowSet, 0, len(s)+1), s...), tablePin{t, r})
|
||||||
|
sort.Slice(out, func(i, j int) bool { return out[i].t.category < out[j].t.category })
|
||||||
|
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.
|
// 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.alt.t != nil && a.alt.t == b.alt.t && a.alt.row != b.alt.row
|
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.
|
||||||
@@ -303,13 +334,13 @@ type pathRead struct {
|
|||||||
type drawVisit struct {
|
type drawVisit struct {
|
||||||
n node
|
n node
|
||||||
group string
|
group string
|
||||||
alt rowAlt
|
alt string
|
||||||
}
|
}
|
||||||
|
|
||||||
type drawKey struct {
|
type drawKey struct {
|
||||||
group string
|
group string
|
||||||
path string
|
path string
|
||||||
alt rowAlt
|
alt string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDrawWalk() *drawWalk {
|
func newDrawWalk() *drawWalk {
|
||||||
@@ -319,7 +350,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, at.alt}
|
v := drawVisit{n, at.group, at.alt.key()}
|
||||||
if w.seen[v] {
|
if w.seen[v] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -330,22 +361,35 @@ func (w *drawWalk) walk(n node, at drawAt) {
|
|||||||
if t, isTemplate := n.(*template); isTemplate && t.drawGroupKey != "" {
|
if t, isTemplate := n.(*template); isTemplate && t.drawGroupKey != "" {
|
||||||
at.group = t.drawGroupKey
|
at.group = t.drawGroupKey
|
||||||
}
|
}
|
||||||
_, isColumn := n.(*column)
|
|
||||||
for _, e := range renderEdges(n) {
|
for _, e := range renderEdges(n) {
|
||||||
// The outermost row is kept: a cell reached through another row's cell renders with it.
|
if cell, isCell := e.to.(*template); isCell && cell.cellOf != nil {
|
||||||
edgeAt := at
|
// A row already entered renders only its own cell of this column.
|
||||||
if cell, _ := e.to.(*template); isColumn && at.alt.t == nil {
|
if r, in := at.alt.rowOf(cell.cellOf); in && r != cell.cellRow {
|
||||||
edgeAt.alt = rowAlt{cell.cellOf, cell.cellRow}
|
continue
|
||||||
}
|
}
|
||||||
w.edge(n, e, edgeAt)
|
w.edge(n, e, drawAt{at.group, at.route, at.alt.enter(cell.cellOf, cell.cellRow)})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.edge(n, e, at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
|
||||||
|
var tr *tableRead
|
||||||
if a, reads := refRead(from, e.label); reads {
|
if a, reads := refRead(from, e.label); reads {
|
||||||
if k := (drawKey{at.group, a.path, at.alt}); !w.read[k] {
|
tr = tableReadOf(from.(*template).fields[a.key], a, e.to)
|
||||||
|
if k := (drawKey{at.group, a.path, at.alt.key()}); !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, tr})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A read that pins the column's table renders that row's cell alone.
|
||||||
|
if c, isColumn := e.to.(*column); isColumn && tr != nil {
|
||||||
|
if r, pinned := tr.pins.pinned(c.t); pinned {
|
||||||
|
if cell := c.t.cellNode(r, c.i); cell != nil {
|
||||||
|
w.walk(cell, drawAt{at.group, at.route, at.alt.enter(c.t, r)})
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.walk(e.to, at)
|
w.walk(e.to, at)
|
||||||
|
|||||||
@@ -240,57 +240,35 @@ func checkFamilies(reads []pathRead) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return replayAlternatives(reads)
|
return replayPairs(reads)
|
||||||
}
|
}
|
||||||
|
|
||||||
// replayAlternatives replays the reads of each draw group into one draws per row
|
// replayPairs replays every two reads of one draw group that can render together into
|
||||||
// alternative — the reads outside any row, then the row's own — so two rows of one
|
// one draws, the earlier read first. A pin conflicts with one earlier pin, never with a
|
||||||
// table are never replayed together.
|
// combination, so pairs find every conflict a full replay would.
|
||||||
func replayAlternatives(reads []pathRead) error {
|
func replayPairs(reads []pathRead) error {
|
||||||
type alt struct {
|
for i, r := range reads {
|
||||||
group string
|
|
||||||
row rowAlt
|
|
||||||
}
|
|
||||||
var alts []alt
|
|
||||||
outside, inside := map[string][]pathRead{}, map[alt][]pathRead{}
|
|
||||||
for _, r := range reads {
|
|
||||||
if r.tr == nil {
|
if r.tr == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if r.at.alt.t == nil {
|
for _, o := range reads[i+1:] {
|
||||||
outside[r.at.group] = append(outside[r.at.group], r)
|
if o.tr == nil || o.at.group != r.at.group || alternatives(r.at, o.at) {
|
||||||
continue
|
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
|
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 conflict(r, err)
|
||||||
|
}
|
||||||
|
if err := o.tr.replay(&d); err != nil {
|
||||||
|
return conflict(o, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// altOrder sorts alternatives by group, the reads outside any row first, then by table and row,
|
func conflict(r pathRead, err error) error {
|
||||||
// so which conflict is reported does not vary.
|
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)
|
||||||
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user