Treat a column's cells as alternatives in the family fence, probe a path before drawing, and record that two categories may name one TSV
This commit is contained in:
@@ -378,7 +378,9 @@ numbers that skews the draw, and `parent` the table a column links to
|
|||||||
columns are the [record](#records)'s columns, so `--format csv` writes the rows and
|
columns are the [record](#records)'s columns, so `--format csv` writes the rows and
|
||||||
`--list` shows `country.alpha2`. A cell is a string node: `1{digits(2)} {digits(2)}`
|
`--list` shows `country.alpha2`. A cell is a string node: `1{digits(2)} {digits(2)}`
|
||||||
in a cell draws digits and `{/misc.uuid}` reads a reference, while `{name}` in a cell
|
in a cell draws digits and `{/misc.uuid}` reads a reference, while `{name}` in a cell
|
||||||
is refused, since a cell has no sibling. `New` proves the header, the options and every
|
is refused, since a cell has no sibling. Each cell may select its own row of another
|
||||||
|
table, `{/misc.currency[SEK].symbol}` on one row and `{/misc.currency[EUR].symbol}`
|
||||||
|
on the next, since only one row renders. `New` proves the header, the options and every
|
||||||
cell token, and refuses a TSV no category names, a key that is empty or repeats, a
|
cell token, and refuses a TSV no category names, a key that is empty or repeats, a
|
||||||
weight that is not a positive number, and a key or name holding `[`, `]`, `{`, `}`,
|
weight that is not a positive number, and a key or name holding `[`, `]`, `{`, `}`,
|
||||||
`"` or `|`, which a selector cannot spell; the rows are indexed on the first draw that
|
`"` or `|`, which a selector cannot spell; the rows are indexed on the first draw that
|
||||||
@@ -956,6 +958,16 @@ renamed or retyped line is a major.
|
|||||||
against the parent's keys and a key's uniqueness is a data mistake, so both are
|
against the parent's keys and a key's uniqueness is a data mistake, so both are
|
||||||
load-time; the name index and the per-parent child lists serve only a draw or a
|
load-time; the name index and the per-parent child lists serve only a draw or a
|
||||||
selection, so they wait for the first one, keeping `New` linear in the bytes read.
|
selection, so they wait for the first one, keeping `New` linear in the bytes read.
|
||||||
|
- **Two categories may name one TSV.** Each is a view of the file with its own
|
||||||
|
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
|
||||||
|
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
|
||||||
|
selecting different rows of another table never meet, as two items of a choice
|
||||||
|
never do; the family fence replays the reads of each cell apart, together with
|
||||||
|
the reads outside any cell. A path is walked once without drawing before it is
|
||||||
|
walked for real, so a path that fails below its first level moves no seeded
|
||||||
|
stream.
|
||||||
- **`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
|
||||||
|
|||||||
@@ -270,10 +270,18 @@ type drawWalk struct {
|
|||||||
seen map[drawVisit]bool
|
seen map[drawVisit]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawAt is where a walk stands: the draw group it draws in, and how the render's root reached it.
|
// 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
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternatives reports whether two reads sit in different cells of one column.
|
||||||
|
func alternatives(a, b drawAt) bool {
|
||||||
|
return a.cell != nil && b.cell != nil && a.cell != b.cell
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -314,7 +322,11 @@ 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) {
|
||||||
|
if isColumn {
|
||||||
|
at.cell = e.to
|
||||||
|
}
|
||||||
w.edge(n, e, at)
|
w.edge(n, e, at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,7 +353,7 @@ func (w *drawWalk) check() error {
|
|||||||
})
|
})
|
||||||
for i, level := range w.reads {
|
for i, level := range w.reads {
|
||||||
for _, into := range w.reads[i+1:] {
|
for _, into := range w.reads[i+1:] {
|
||||||
if into.at.group != level.at.group {
|
if into.at.group != level.at.group || alternatives(level.at, into.at) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(into.a.path, level.a.path+".") && !(level.tr != nil && level.tr.whole) {
|
if strings.HasPrefix(into.a.path, level.a.path+".") && !(level.tr != nil && level.tr.whole) {
|
||||||
|
|||||||
@@ -227,28 +227,49 @@ func (r *tableRead) selected(t *table) (tableSel, bool) {
|
|||||||
// a table one read draws that another pins, and two reads pinning different rows.
|
// 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.
|
// The reads come sorted by group and path, so which pair is reported does not vary.
|
||||||
func checkFamilies(reads []pathRead) error {
|
func checkFamilies(reads []pathRead) error {
|
||||||
shared := map[string]*draws{}
|
|
||||||
for i, r := range reads {
|
for i, r := range reads {
|
||||||
if r.tr == nil {
|
if r.tr == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, o := range reads[:i] {
|
for _, o := range reads[:i] {
|
||||||
if o.tr == nil || o.at.group != r.at.group || o.tr.head.family() != r.tr.head.family() {
|
if o.tr == nil || o.at.group != r.at.group || alternatives(o.at, r.at) || o.tr.head.family() != r.tr.head.family() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := checkFamilyPair(o, r); err != nil {
|
if err := checkFamilyPair(o, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d := shared[r.at.group]
|
|
||||||
if d == nil {
|
|
||||||
d = &draws{}
|
|
||||||
shared[r.at.group] = d
|
|
||||||
}
|
}
|
||||||
if err := r.tr.replay(d); err != nil {
|
return replayAlternatives(reads)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replayAlternatives replays the reads of each draw group into one draws per cell
|
||||||
|
// alternative — the reads outside any cell, then the cell's own — so two cells of
|
||||||
|
// one column are never replayed together.
|
||||||
|
func replayAlternatives(reads []pathRead) error {
|
||||||
|
type alt struct {
|
||||||
|
group string
|
||||||
|
cell node
|
||||||
|
}
|
||||||
|
var alts []alt
|
||||||
|
seen := map[alt]bool{}
|
||||||
|
for _, r := range reads {
|
||||||
|
if a := (alt{r.at.group, r.at.cell}); r.tr != nil && !seen[a] {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,12 +189,15 @@ func (w pathWalk) atLeaf(n node) error {
|
|||||||
// by the pins' session where the walk names no choice action.
|
// by the pins' session where the walk names no choice action.
|
||||||
func walkChoice(c *choice, tail []string, w pathWalk) (node, error) {
|
func walkChoice(c *choice, tail []string, w pathWalk) (node, error) {
|
||||||
if w.choice == nil {
|
if w.choice == nil {
|
||||||
if w.pins == nil || w.pins.s == nil {
|
if w.pins == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err := carriedByAll(c, tail); err != nil {
|
if err := carriedByAll(c, tail); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if w.pins.s == nil { // a probe: every variant carries the tail, so any one proves it
|
||||||
|
return walkPath(c.items[0], tail, w)
|
||||||
|
}
|
||||||
return walkPath(pick(w.pins.s, c), tail, w)
|
return walkPath(pick(w.pins.s, c), tail, w)
|
||||||
}
|
}
|
||||||
next, err := w.choice(c, tail)
|
next, err := w.choice(c, tail)
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
// carries before a variant is picked — a path that resolves at all resolves on
|
// carries before a variant is picked — a path that resolves at all resolves on
|
||||||
// every call.
|
// every call.
|
||||||
func descend(s *session, root node, segments []string, sc drawScope) (node, error) {
|
func descend(s *session, root node, segments []string, sc drawScope) (node, error) {
|
||||||
|
// Walked once without drawing first, so a path that fails moves no seeded stream.
|
||||||
|
var probe draws
|
||||||
|
if _, err := walkPath(root, segments, pathWalk{pins: &probe}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return walkPath(root, segments, pathWalk{pins: sc.draws(s)})
|
return walkPath(root, segments, pathWalk{pins: sc.draws(s)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user