Gather the family fence beside drawWalk.check in drawfence.go
This commit is contained in:
@@ -879,8 +879,7 @@ 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 and a record's columns, and the walk over what a render reads
|
drawfence.go the load fences over reference draws: a drawGroup's, a render's, a record's columns and a table family's, and the walk over what a render reads
|
||||||
family.go a family of linked tables: what a reference path reads of one, and the fences over paths into it
|
|
||||||
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
|
||||||
|
|||||||
+200
@@ -309,6 +309,90 @@ func (w *drawWalk) check() error {
|
|||||||
return checkFamilies(w.reads)
|
return checkFamilies(w.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.family() != r.tr.headTable.family() {
|
||||||
|
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.category}, 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.category, 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.category, 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.
|
||||||
|
// docs/decisions.md#the-rows-of-a-table-are-alternatives
|
||||||
|
func alternatives(a, b drawAt) bool {
|
||||||
|
return a.pins.differs(&b.pins) || a.wholePins.differs(&b.wholePins)
|
||||||
|
}
|
||||||
|
|
||||||
func overlapError(route drawRoute, ref string, into pathRead) error {
|
func overlapError(route drawRoute, 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))
|
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))
|
||||||
}
|
}
|
||||||
@@ -321,3 +405,119 @@ func (r drawRoute) spelled(ref string) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s with {%s}", r.spelling, ref)
|
return fmt.Sprintf("%s with {%s}", r.spelling, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tableRead is what a reference path reads of a table family: the table its head
|
||||||
|
// names, the rows its selectors pin, the tables it draws — those it walks with no
|
||||||
|
// row pinned, and their unpinned ancestors — each selector's spelling, and whether
|
||||||
|
// it lands on a `tableRow`.
|
||||||
|
type tableRead struct {
|
||||||
|
headTable *table
|
||||||
|
pins pinSet
|
||||||
|
drawn map[*table]bool
|
||||||
|
sels []tableSel
|
||||||
|
landsRow bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// tableSel is one selector on the way: the table it selects a row of, and the path
|
||||||
|
// as written up to and including it.
|
||||||
|
type tableSel struct {
|
||||||
|
t *table
|
||||||
|
spelling string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tableReadOf replays a reference path through pathProbe, so two paths pinning one
|
||||||
|
// row by different routes compare equal. checkPath proved
|
||||||
|
// each selector names a row.
|
||||||
|
func tableReadOf(head node, a arm, leaf node) *tableRead {
|
||||||
|
t, isTable := head.(*table)
|
||||||
|
if !isTable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
probe := &pathProbe{drawn: map[*table]bool{}}
|
||||||
|
_, _ = probe.walk(t, a.tail)
|
||||||
|
tr := &tableRead{headTable: t, pins: probe.pins, drawn: probe.drawn}
|
||||||
|
written := a.spelling[:len(a.spelling)-len(joinSegments(a.tail))]
|
||||||
|
cur := t
|
||||||
|
for i, seg := range a.tail {
|
||||||
|
switch d := cur.descendant(seg); {
|
||||||
|
case isSelector(seg):
|
||||||
|
tr.sels = append(tr.sels, tableSel{cur, written + joinSegments(a.tail[:i+1])})
|
||||||
|
case d != nil:
|
||||||
|
cur = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, tr.landsRow = leaf.(*tableRow)
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
// selected is the selector in r on t, or on the nearest ancestor of t it selects.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawnOf is a table the read draws that pins holds a row of, if any.
|
||||||
|
func (r *tableRead) drawnOf(pins *pinSet) *table {
|
||||||
|
var found *table
|
||||||
|
pins.each(func(t *table, _ int) {
|
||||||
|
if found == nil && r.drawn[t] {
|
||||||
|
found = t
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// replay pins the read's rows into d, where they agree with the rows pinned before.
|
||||||
|
func (r *tableRead) replay(d *pinSet) error {
|
||||||
|
var err error
|
||||||
|
r.pins.each(func(t *table, row int) {
|
||||||
|
if err == nil {
|
||||||
|
err = d.pinRow(t, row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkOwnFamily refuses a table's format or cell that reads, however many templates
|
||||||
|
// away and through a repeat or a draw group too, a table of its own family: a whole
|
||||||
|
// read draws its row without pinning it, so the family would draw apart from the row
|
||||||
|
// it renders, whichever draws the reaching template holds.
|
||||||
|
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
|
||||||
|
func checkOwnFamily(t *template) error {
|
||||||
|
own := t.table
|
||||||
|
if own == nil {
|
||||||
|
own = t.cellOf
|
||||||
|
}
|
||||||
|
if own == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
seen := map[node]bool{}
|
||||||
|
var find func(n node) (renderEdge, *table, bool)
|
||||||
|
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).head(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 bare read of %s would draw apart from the row it renders; read the family from a template beside it, or add the value as a column", e.reached(), head.category, own.category)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,226 +0,0 @@
|
|||||||
package fejkdata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// tableRead is what a reference path reads of a table family: the table its head
|
|
||||||
// names, the rows its selectors pin, the tables it draws — those it walks with no
|
|
||||||
// row pinned, and their unpinned ancestors — each selector's spelling, and whether
|
|
||||||
// it lands on a `tableRow`.
|
|
||||||
type tableRead struct {
|
|
||||||
headTable *table
|
|
||||||
pins pinSet
|
|
||||||
drawn map[*table]bool
|
|
||||||
sels []tableSel
|
|
||||||
landsRow bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// tableSel is one selector on the way: the table it selects a row of, and the path
|
|
||||||
// as written up to and including it.
|
|
||||||
type tableSel struct {
|
|
||||||
t *table
|
|
||||||
spelling string
|
|
||||||
}
|
|
||||||
|
|
||||||
// tableReadOf replays a reference path through pathProbe, so two paths pinning one
|
|
||||||
// row by different routes compare equal. checkPath proved
|
|
||||||
// each selector names a row.
|
|
||||||
func tableReadOf(head node, a arm, leaf node) *tableRead {
|
|
||||||
t, isTable := head.(*table)
|
|
||||||
if !isTable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
probe := &pathProbe{drawn: map[*table]bool{}}
|
|
||||||
_, _ = probe.walk(t, a.tail)
|
|
||||||
tr := &tableRead{headTable: t, pins: probe.pins, drawn: probe.drawn}
|
|
||||||
written := a.spelling[:len(a.spelling)-len(joinSegments(a.tail))]
|
|
||||||
cur := t
|
|
||||||
for i, seg := range a.tail {
|
|
||||||
switch d := cur.descendant(seg); {
|
|
||||||
case isSelector(seg):
|
|
||||||
tr.sels = append(tr.sels, tableSel{cur, written + joinSegments(a.tail[:i+1])})
|
|
||||||
case d != nil:
|
|
||||||
cur = d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, tr.landsRow = leaf.(*tableRow)
|
|
||||||
return tr
|
|
||||||
}
|
|
||||||
|
|
||||||
// joinSegments spells segments as a path: a selector attaches to the name before it.
|
|
||||||
func joinSegments(segs []string) string {
|
|
||||||
var b strings.Builder
|
|
||||||
for _, s := range segs {
|
|
||||||
if b.Len() > 0 && !isSelector(s) {
|
|
||||||
b.WriteByte('.')
|
|
||||||
}
|
|
||||||
b.WriteString(s)
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 on t, or on the nearest ancestor of t it selects.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.family() != r.tr.headTable.family() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkFamilyPair(o, r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return replayPairs(reads)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// replay pins the read's rows into d, where they agree with the rows pinned before.
|
|
||||||
func (r *tableRead) replay(d *pinSet) error {
|
|
||||||
var err error
|
|
||||||
r.pins.each(func(t *table, row int) {
|
|
||||||
if err == nil {
|
|
||||||
err = d.pinRow(t, row)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.category}, 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.category, 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.category, y.at.route.spelled(y.a.spelling))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// drawnOf is a table the read draws that pins holds a row of, if any.
|
|
||||||
func (r *tableRead) drawnOf(pins *pinSet) *table {
|
|
||||||
var found *table
|
|
||||||
pins.each(func(t *table, _ int) {
|
|
||||||
if found == nil && r.drawn[t] {
|
|
||||||
found = t
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkOwnFamily refuses a table's format or cell that reads, however many templates
|
|
||||||
// away and through a repeat or a draw group too, a table of its own family: a whole
|
|
||||||
// read draws its row without pinning it, so the family would draw apart from the row
|
|
||||||
// it renders, whichever draws the reaching template holds.
|
|
||||||
// docs/decisions.md#a-table-never-reaches-its-own-family-by-any-route
|
|
||||||
func checkOwnFamily(t *template) error {
|
|
||||||
own := t.table
|
|
||||||
if own == nil {
|
|
||||||
own = t.cellOf
|
|
||||||
}
|
|
||||||
if own == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
seen := map[node]bool{}
|
|
||||||
var find func(n node) (renderEdge, *table, bool)
|
|
||||||
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).head(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 bare read of %s would draw apart from the row it renders; read the family from a template beside it, or add the value as a column", e.reached(), head.category, own.category)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// docs/decisions.md#the-rows-of-a-table-are-alternatives
|
|
||||||
func alternatives(a, b drawAt) bool {
|
|
||||||
return a.pins.differs(&b.pins) || a.wholePins.differs(&b.wholePins)
|
|
||||||
}
|
|
||||||
@@ -456,3 +456,15 @@ func checkPath(n node, tail []string, level string) error {
|
|||||||
_, err := (&pathCheck{level: level, tail: tail}).run(n)
|
_, err := (&pathCheck{level: level, tail: tail}).run(n)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// joinSegments spells segments as a path: a selector attaches to the name before it.
|
||||||
|
func joinSegments(segs []string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, s := range segs {
|
||||||
|
if b.Len() > 0 && !isSelector(s) {
|
||||||
|
b.WriteByte('.')
|
||||||
|
}
|
||||||
|
b.WriteString(s)
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|||||||
@@ -613,3 +613,11 @@ func (t *table) selectorSpelling(r int) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s line %d", t.file, r+2)
|
return fmt.Sprintf("%s line %d", t.file, r+2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// family is the table a chain of parents ends at.
|
||||||
|
func (t *table) family() *table {
|
||||||
|
for t.parentT != nil {
|
||||||
|
t = t.parentT
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Gather the family fence beside `drawWalk.check`: `checkFamilies`, `replayPairs`,
|
|
||||||
`checkFamilyPair` and `tableRead.replay` sit in `family.go` and `pins.go`, and one
|
|
||||||
rule spans seven files.
|
|
||||||
- Compile every format in one phase: a template holding a reference has nil `ops`
|
- Compile every format in one phase: a template holding a reference has nil `ops`
|
||||||
until `linkTemplateRefs`, so a check reading `ops` before link skips it silently.
|
until `linkTemplateRefs`, so a check reading `ops` before link skips it silently.
|
||||||
- Run `New`, `NewTemplate` and `FakeStruct` through one bind pipeline that names
|
- Run `New`, `NewTemplate` and `FakeStruct` through one bind pipeline that names
|
||||||
@@ -44,7 +41,7 @@ This round, in order:
|
|||||||
path `Fake` accepts, which a selector is too. Delete the dead `table.descends` and
|
path `Fake` accepts, which a selector is too. Delete the dead `table.descends` and
|
||||||
the redundant `string(key)` in `checkNoRepeatedItem`.
|
the redundant `string(key)` in `checkNoRepeatedItem`.
|
||||||
- Delete the decision "The rows of a table are alternatives": it defines `alternatives`,
|
- Delete the decision "The rows of a table are alternatives": it defines `alternatives`,
|
||||||
which its comment in `family.go` already does. Point the README's "which pairs are
|
which its comment in `drawfence.go` already does. Point the README's "which pairs are
|
||||||
weighed against each other" at that comment's rule, stated in the README's Table
|
weighed against each other" at that comment's rule, stated in the README's Table
|
||||||
section, where it sends a reader to the log for the definition today.
|
section, where it sends a reader to the log for the definition today.
|
||||||
- Run the nine-seat panel again and file what it names here.
|
- Run the nine-seat panel again and file what it names here.
|
||||||
|
|||||||
Reference in New Issue
Block a user