Replay each path's selectors through the walk for the family fence, require a key for a name, refuse a name shadowing a key, a table reading its own family and an ancestor's column clash, add the row node and family.go, and pin eight rows inline
This commit is contained in:
@@ -21,110 +21,6 @@ type drawScope struct {
|
||||
row int
|
||||
}
|
||||
|
||||
// pinned is the row the render pinned for t, if any.
|
||||
func (d *draws) pinned(t *table) (int, bool) {
|
||||
for _, p := range d.pins[:d.npins] {
|
||||
if p.t == t {
|
||||
return p.row, true
|
||||
}
|
||||
}
|
||||
r, ok := d.more[t]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// mustRow is the row pinned for t, which the walk reaching a column pinned.
|
||||
func (d *draws) mustRow(t *table) int {
|
||||
r, ok := d.pinned(t)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("fejkdata: a column of %s is rendered with no row pinned", t.category))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// pin pins row r of t, and the rows of t's ancestors it links to.
|
||||
func (d *draws) pin(t *table, r int) {
|
||||
for {
|
||||
if _, done := d.pinned(t); done {
|
||||
return
|
||||
}
|
||||
if d.npins < len(d.pins) {
|
||||
d.pins[d.npins] = tablePin{t, r}
|
||||
d.npins++
|
||||
} else {
|
||||
if d.more == nil {
|
||||
d.more = map[*table]int{}
|
||||
}
|
||||
d.more[t] = r
|
||||
}
|
||||
if t.parentT == nil {
|
||||
return
|
||||
}
|
||||
t, r = t.parentT, t.parentRow(r)
|
||||
}
|
||||
}
|
||||
|
||||
// rowOf is the render's row of t: the one pinned, else one drawn inside the
|
||||
// nearest pinned ancestor — its parent drawn inside that first where the ancestor
|
||||
// is further up — or over the whole table, and pinned with its ancestors.
|
||||
func (d *draws) rowOf(t *table) int {
|
||||
if r, ok := d.pinned(t); ok {
|
||||
return r
|
||||
}
|
||||
r := -1
|
||||
for a := t.parentT; a != nil && r < 0; a = a.parentT {
|
||||
if _, ok := d.pinned(a); !ok {
|
||||
continue
|
||||
}
|
||||
if t.parentT != a {
|
||||
d.rowOf(t.parentT)
|
||||
}
|
||||
pr, _ := d.pinned(t.parentT)
|
||||
r = t.drawUnder(d.s, pr)
|
||||
}
|
||||
if r < 0 {
|
||||
r = t.draw(d.s)
|
||||
}
|
||||
d.pin(t, r)
|
||||
return r
|
||||
}
|
||||
|
||||
// selectRow pins the row a selector names, refusing one outside the rows pinned
|
||||
// before it.
|
||||
func (d *draws) selectRow(t *table, sel string) error {
|
||||
r, err := t.find(sel, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pr, ok := d.pinned(t); ok && pr != r {
|
||||
return fmt.Errorf("%s is not %s, the row already drawn", t.selectorSpelling(r), t.selectorSpelling(pr))
|
||||
}
|
||||
for a := t.parentT; a != nil; a = a.parentT {
|
||||
if pa, ok := d.pinned(a); ok && !t.under(r, a, pa) {
|
||||
return fmt.Errorf("%s is not inside %s", t.selectorSpelling(r), a.selectorSpelling(pa))
|
||||
}
|
||||
}
|
||||
d.pin(t, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// inside keeps the rows of t that sit inside every pinned ancestor.
|
||||
func (d *draws) inside(t *table, rows []int) []int {
|
||||
for a := t.parentT; a != nil; a = a.parentT {
|
||||
pa, ok := d.pinned(a)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var kept []int
|
||||
for _, r := range rows {
|
||||
if t.under(r, a, pa) {
|
||||
kept = append(kept, r)
|
||||
}
|
||||
}
|
||||
rows = kept
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// newDrawSet makes the unnamed group's maps where the set is declared, keeping them on that frame's
|
||||
// stack for a render that reads through them; a zero drawSet makes them on its first read instead.
|
||||
func newDrawSet(s *session) drawSet {
|
||||
@@ -368,78 +264,6 @@ type pathRead struct {
|
||||
tr *tableRead // set where the reference names a table
|
||||
}
|
||||
|
||||
// tableRead is what a reference reads of a table family: the table named, the rows
|
||||
// its selectors pin along the way, and whether it lands on a row rendered whole.
|
||||
type tableRead struct {
|
||||
head *table
|
||||
sels []tableSel
|
||||
whole bool
|
||||
}
|
||||
|
||||
// tableSel is one selector on the way: the table it selects a row of, and the
|
||||
// selector's spelling up to its closing bracket.
|
||||
type tableSel struct {
|
||||
t *table
|
||||
row int
|
||||
spelling string
|
||||
}
|
||||
|
||||
// tableReadOf reads what a reference path does of a table, replaying its selectors
|
||||
// over a walk of its own — checkPath proved each names a row — so two paths naming
|
||||
// one row by key and by name compare equal.
|
||||
func tableReadOf(head node, a arm, leaf node) *tableRead {
|
||||
t, isTable := head.(*table)
|
||||
if !isTable {
|
||||
return nil
|
||||
}
|
||||
tr := &tableRead{head: t}
|
||||
var pins draws
|
||||
cur, seen := t, a.name
|
||||
for _, seg := range a.tail {
|
||||
switch {
|
||||
case isSelector(seg):
|
||||
end := strings.Index(seen, "]") + 1
|
||||
_ = pins.selectRow(cur, selectorOf(seg))
|
||||
row, _ := pins.pinned(cur)
|
||||
tr.sels = append(tr.sels, tableSel{cur, row, a.name[:len(a.name)-len(seen)+end]})
|
||||
seen = seen[end:]
|
||||
case cur.children[seg] != nil:
|
||||
cur = cur.children[seg]
|
||||
}
|
||||
}
|
||||
c, isColumn := leaf.(*column)
|
||||
tr.whole = isColumn && c.i < 0
|
||||
return tr
|
||||
}
|
||||
|
||||
// family is the table a chain of parents ends at.
|
||||
func (t *table) family() *table {
|
||||
for t.parentT != nil {
|
||||
t = t.parentT
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// selected is the selector in r that pins t, or the nearest ancestor of t it pins.
|
||||
func (r *tableRead) selected(t *table) (tableSel, bool) {
|
||||
for ; t != nil; t = t.parentT {
|
||||
for _, s := range r.sels {
|
||||
if s.t == t {
|
||||
return s, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return tableSel{}, false
|
||||
}
|
||||
|
||||
func (r *tableRead) selection() string {
|
||||
parts := make([]string, len(r.sels))
|
||||
for i, s := range r.sels {
|
||||
parts[i] = fmt.Sprintf("%s[%d]", s.t.category, s.row)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
type drawVisit struct {
|
||||
n node
|
||||
group string
|
||||
@@ -501,45 +325,9 @@ func (w *drawWalk) check() error {
|
||||
if strings.HasPrefix(into.a.path, level.a.path+".") && !(level.tr != nil && level.tr.whole) {
|
||||
return overlapError(level.at.route, level.a.name, into)
|
||||
}
|
||||
if err := checkFamily(level, into); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkFamily refuses two reads of one table family in one group that cannot read one consistent
|
||||
// draw: a table rendered whole beside a path into the family, and two paths selecting different rows.
|
||||
func checkFamily(a, b pathRead) error {
|
||||
if a.tr == nil || b.tr == nil || a.tr.head.family() != b.tr.head.family() {
|
||||
return nil
|
||||
}
|
||||
for _, pair := range [][2]pathRead{{a, b}, {b, a}} {
|
||||
bare, path := pair[0], pair[1]
|
||||
if len(bare.a.tail) == 0 && len(path.a.tail) > 0 {
|
||||
return overlapError(bare.at.route, bare.a.name, path)
|
||||
}
|
||||
}
|
||||
if len(a.a.tail) == 0 || len(b.a.tail) == 0 || a.tr.selection() == b.tr.selection() {
|
||||
return nil
|
||||
}
|
||||
for _, pair := range [][2]pathRead{{a, b}, {b, a}} {
|
||||
selected, plain := pair[0], pair[1]
|
||||
if len(plain.tr.sels) > 0 {
|
||||
continue
|
||||
}
|
||||
if s, ok := selected.tr.selected(plain.tr.head); ok {
|
||||
tail := plain.a.tail
|
||||
if s.t != plain.tr.head {
|
||||
tail = append([]string{plain.tr.head.category}, tail...)
|
||||
}
|
||||
return fmt.Errorf("%s reads %s without the row %s selects; write {%s.%s}, or draw them apart with a drawGroup",
|
||||
plain.at.route.spelled(plain.a.name), plain.tr.head.category, selected.at.route.spelled(selected.a.name), s.spelling, strings.Join(tail, "."))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s and %s select different rows of one table family; select the same rows in both, or draw them apart with a drawGroup",
|
||||
a.at.route.spelled(a.a.name), b.at.route.spelled(b.a.name))
|
||||
return checkFamilies(w.reads)
|
||||
}
|
||||
|
||||
func overlapError(route drawRoute, ref string, into pathRead) error {
|
||||
|
||||
Reference in New Issue
Block a user