Move readSurvey's verdict from drawfence.go into readsurvey.go
This commit is contained in:
@@ -886,8 +886,8 @@ format.go the {token} grammar: scanning, tokens, operands, validation, com
|
|||||||
hold.go the hold: one draw per held name, and reading a field through it
|
hold.go the hold: one draw per held name, and reading a field through it
|
||||||
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: a drawGroup's, a render's, a record's columns and a table family's
|
drawfence.go the load fences over reference draws: a drawGroup's, a table's own family, and a render's and a record's columns through the survey
|
||||||
readsurvey.go the load-time survey of what a render reads, and what a path reads of a table family, for the draw fences
|
readsurvey.go the load-time survey of what a render reads, and what a path reads of a table family, and its verdict on them
|
||||||
pins.go the pin set: the table rows a render fixes, which the draw fences replay
|
pins.go the pin set: the table rows a render fixes, which the draw fences replay
|
||||||
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
|
||||||
|
|||||||
+1
-115
@@ -1,10 +1,6 @@
|
|||||||
package fejkdata
|
package fejkdata
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -193,113 +189,3 @@ func readsTable(n node, a arm) bool {
|
|||||||
|
|
||||||
// checkColumnDraws fences a record's columns as one render.
|
// checkColumnDraws fences a record's columns as one render.
|
||||||
func checkColumnDraws(t *template, columns []string) error { return surveyColumns(t, columns).check() }
|
func checkColumnDraws(t *template, columns []string) error { return surveyColumns(t, columns).check() }
|
||||||
|
|
||||||
// check refuses what one draw per reference path cannot answer for: a read of a level beside a path
|
|
||||||
// another read takes into it, and two reads of one table family that select different rows. Reads
|
|
||||||
// are compared in path order, so which pair is reported does not vary.
|
|
||||||
func (s *readSurvey) check() error {
|
|
||||||
sort.SliceStable(s.reads, func(i, j int) bool {
|
|
||||||
if s.reads[i].at.group != s.reads[j].at.group {
|
|
||||||
return s.reads[i].at.group < s.reads[j].at.group
|
|
||||||
}
|
|
||||||
return s.reads[i].a.path < s.reads[j].a.path
|
|
||||||
})
|
|
||||||
for i, level := range s.reads {
|
|
||||||
for _, into := range s.reads[i+1:] {
|
|
||||||
if into.at.group != level.at.group || alternatives(level.at, into.at) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(into.a.path, level.a.path+".") && !(level.tr != nil && level.tr.landsRow) {
|
|
||||||
return overlapError(level.at.route, level.a.spelling, into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return checkFamilies(s.reads)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkFamilies refuses reads of one table family in one draw group that cannot
|
|
||||||
// read one consistent draw: a table rendered whole beside a path into the family,
|
|
||||||
// a table one read draws that another pins, and two reads pinning different rows.
|
|
||||||
// The reads come sorted by group and path, so which pair is reported does not vary.
|
|
||||||
func checkFamilies(reads []pathRead) error {
|
|
||||||
for i, r := range reads {
|
|
||||||
if r.tr == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, o := range reads[:i] {
|
|
||||||
if o.tr == nil || o.at.group != r.at.group || alternatives(o.at, r.at) || o.tr.headTable.familyRoot() != r.tr.headTable.familyRoot() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkFamilyPair(o, r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return replayPairs(reads)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkFamilyPair refuses a table read whole beside a path into its family, and a
|
|
||||||
// table one read draws that the other pins, since which token renders first would
|
|
||||||
// then decide the row.
|
|
||||||
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
|
|
||||||
func checkFamilyPair(a, b pathRead) error {
|
|
||||||
for _, pair := range [][2]pathRead{{a, b}, {b, a}} {
|
|
||||||
x, y := pair[0], pair[1]
|
|
||||||
if len(x.a.tail) == 0 && len(y.a.tail) > 0 {
|
|
||||||
return overlapError(x.at.route, x.a.spelling, y)
|
|
||||||
}
|
|
||||||
if drawn := x.tr.drawnOf(&y.tr.pins); drawn != nil {
|
|
||||||
if s, ok := y.tr.selected(x.tr.headTable); ok && len(x.tr.sels) == 0 {
|
|
||||||
tail := x.a.tail
|
|
||||||
if s.t != x.tr.headTable {
|
|
||||||
tail = append([]string{x.tr.headTable.segment}, tail...)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%s draws %s, which %s selects a row of; write {%s.%s}, or draw them apart with a drawGroup",
|
|
||||||
x.at.route.spelled(x.a.spelling), drawn.segment, y.at.route.spelled(y.a.spelling), s.spelling, joinSegments(tail))
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%s draws %s, which %s selects a row of; select that row in both, or draw them apart with a drawGroup",
|
|
||||||
x.at.route.spelled(x.a.spelling), drawn.segment, y.at.route.spelled(y.a.spelling))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// replayPairs replays every two reads that can render together into one draws, the earlier
|
|
||||||
// read first. Pairs find every conflict a full replay would: clash judges a row against one
|
|
||||||
// pinned table, and the read that pinned it holds that pin itself, since a read's pins carry
|
|
||||||
// its rows' ancestors, so the pair of those two reads clashes the same way.
|
|
||||||
func replayPairs(reads []pathRead) error {
|
|
||||||
for i, r := range reads {
|
|
||||||
if r.tr == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, o := range reads[i+1:] {
|
|
||||||
if o.tr == nil || o.at.group != r.at.group || alternatives(r.at, o.at) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var d pinSet
|
|
||||||
if err := r.tr.replay(&d); err != nil {
|
|
||||||
return conflict(r, err)
|
|
||||||
}
|
|
||||||
if err := o.tr.replay(&d); err != nil {
|
|
||||||
return conflict(o, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func conflict(r pathRead, err error) error {
|
|
||||||
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.spelling), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// alternatives reports whether two reads never render together: one read renders one row of a
|
|
||||||
// table, so reads under different rows the walks pinned, or under different rows of one whole
|
|
||||||
// draw, never meet.
|
|
||||||
func alternatives(a, b surveyAt) bool {
|
|
||||||
return a.pins.differs(&b.pins) || a.wholePins.differs(&b.wholePins)
|
|
||||||
}
|
|
||||||
|
|
||||||
func overlapError(route surveyRoute, ref string, into pathRead) error {
|
|
||||||
return fmt.Errorf("%s renders a level that %s reads a path into; name the fields you want instead, or draw them apart with a drawGroup", route.spelled(ref), into.at.route.spelled(into.a.spelling))
|
|
||||||
}
|
|
||||||
|
|||||||
+111
@@ -3,6 +3,7 @@ package fejkdata
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// readSurvey gathers, at load, the references one render reads, by draw group, for check to compare.
|
// readSurvey gathers, at load, the references one render reads, by draw group, for check to compare.
|
||||||
@@ -240,3 +241,113 @@ func (r *tableRead) replay(d *pinSet) error {
|
|||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check refuses what one draw per reference path cannot answer for: a read of a level beside a path
|
||||||
|
// another read takes into it, and two reads of one table family that select different rows. Reads
|
||||||
|
// are compared in path order, so which pair is reported does not vary.
|
||||||
|
func (s *readSurvey) check() error {
|
||||||
|
sort.SliceStable(s.reads, func(i, j int) bool {
|
||||||
|
if s.reads[i].at.group != s.reads[j].at.group {
|
||||||
|
return s.reads[i].at.group < s.reads[j].at.group
|
||||||
|
}
|
||||||
|
return s.reads[i].a.path < s.reads[j].a.path
|
||||||
|
})
|
||||||
|
for i, level := range s.reads {
|
||||||
|
for _, into := range s.reads[i+1:] {
|
||||||
|
if into.at.group != level.at.group || alternatives(level.at, into.at) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(into.a.path, level.a.path+".") && !(level.tr != nil && level.tr.landsRow) {
|
||||||
|
return overlapError(level.at.route, level.a.spelling, into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkFamilies(s.reads)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFamilies refuses reads of one table family in one draw group that cannot
|
||||||
|
// read one consistent draw: a table rendered whole beside a path into the family,
|
||||||
|
// a table one read draws that another pins, and two reads pinning different rows.
|
||||||
|
// The reads come sorted by group and path, so which pair is reported does not vary.
|
||||||
|
func checkFamilies(reads []pathRead) error {
|
||||||
|
for i, r := range reads {
|
||||||
|
if r.tr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, o := range reads[:i] {
|
||||||
|
if o.tr == nil || o.at.group != r.at.group || alternatives(o.at, r.at) || o.tr.headTable.familyRoot() != r.tr.headTable.familyRoot() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := checkFamilyPair(o, r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replayPairs(reads)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFamilyPair refuses a table read whole beside a path into its family, and a
|
||||||
|
// table one read draws that the other pins, since which token renders first would
|
||||||
|
// then decide the row.
|
||||||
|
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
|
||||||
|
func checkFamilyPair(a, b pathRead) error {
|
||||||
|
for _, pair := range [][2]pathRead{{a, b}, {b, a}} {
|
||||||
|
x, y := pair[0], pair[1]
|
||||||
|
if len(x.a.tail) == 0 && len(y.a.tail) > 0 {
|
||||||
|
return overlapError(x.at.route, x.a.spelling, y)
|
||||||
|
}
|
||||||
|
if drawn := x.tr.drawnOf(&y.tr.pins); drawn != nil {
|
||||||
|
if s, ok := y.tr.selected(x.tr.headTable); ok && len(x.tr.sels) == 0 {
|
||||||
|
tail := x.a.tail
|
||||||
|
if s.t != x.tr.headTable {
|
||||||
|
tail = append([]string{x.tr.headTable.segment}, tail...)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s draws %s, which %s selects a row of; write {%s.%s}, or draw them apart with a drawGroup",
|
||||||
|
x.at.route.spelled(x.a.spelling), drawn.segment, y.at.route.spelled(y.a.spelling), s.spelling, joinSegments(tail))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s draws %s, which %s selects a row of; select that row in both, or draw them apart with a drawGroup",
|
||||||
|
x.at.route.spelled(x.a.spelling), drawn.segment, y.at.route.spelled(y.a.spelling))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// replayPairs replays every two reads that can render together into one draws, the earlier
|
||||||
|
// read first. Pairs find every conflict a full replay would: clash judges a row against one
|
||||||
|
// pinned table, and the read that pinned it holds that pin itself, since a read's pins carry
|
||||||
|
// its rows' ancestors, so the pair of those two reads clashes the same way.
|
||||||
|
func replayPairs(reads []pathRead) error {
|
||||||
|
for i, r := range reads {
|
||||||
|
if r.tr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, o := range reads[i+1:] {
|
||||||
|
if o.tr == nil || o.at.group != r.at.group || alternatives(r.at, o.at) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var d pinSet
|
||||||
|
if err := r.tr.replay(&d); err != nil {
|
||||||
|
return conflict(r, err)
|
||||||
|
}
|
||||||
|
if err := o.tr.replay(&d); err != nil {
|
||||||
|
return conflict(o, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func conflict(r pathRead, err error) error {
|
||||||
|
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.spelling), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternatives reports whether two reads never render together: one read renders one row of a
|
||||||
|
// table, so reads under different rows the walks pinned, or under different rows of one whole
|
||||||
|
// draw, never meet.
|
||||||
|
func alternatives(a, b surveyAt) bool {
|
||||||
|
return a.pins.differs(&b.pins) || a.wholePins.differs(&b.wholePins)
|
||||||
|
}
|
||||||
|
|
||||||
|
func overlapError(route surveyRoute, ref string, into pathRead) error {
|
||||||
|
return fmt.Errorf("%s renders a level that %s reads a path into; name the fields you want instead, or draw them apart with a drawGroup", route.spelled(ref), into.at.route.spelled(into.a.spelling))
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ ranking `readSurvey.walk` hardest and all seven the unit they would least modify
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Move `readSurvey`'s verdict — `check`, `checkFamilies`, `checkFamilyPair`,
|
|
||||||
`replayPairs`, `alternatives`, `conflict` and `overlapError` — from `drawfence.go`
|
|
||||||
into `readsurvey.go`: both architects looked for it beside the survey.
|
|
||||||
- Record each selector's table and spelling in `pathProbe.readRow`, and delete
|
- Record each selector's table and spelling in `pathProbe.readRow`, and delete
|
||||||
`tableReadOf`'s second walk over the tail: it steps by `descendant` apart from
|
`tableReadOf`'s second walk over the tail: it steps by `descendant` apart from
|
||||||
`route`, so a route that steps up to a parent row credits a selector to the wrong table.
|
`route`, so a route that steps up to a parent row credits a selector to the wrong table.
|
||||||
|
|||||||
Reference in New Issue
Block a user