Split walkPath's four modes into walkers of their own, sharing how a path steps
This commit is contained in:
@@ -32,8 +32,9 @@ func tableReadOf(head node, a arm, leaf node) *tableRead {
|
|||||||
if !isTable {
|
if !isTable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
tr := &tableRead{headTable: t, drawn: map[*table]bool{}}
|
probe := &probeWalk{}
|
||||||
_, _ = walkPath(t, a.tail, pathWalk{mode: walkProbe, pins: &tr.pins, drawn: tr.drawn})
|
_, _ = 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))]
|
written := a.spelling[:len(a.spelling)-len(joinSegments(a.tail))]
|
||||||
cur := t
|
cur := t
|
||||||
for i, seg := range a.tail {
|
for i, seg := range a.tail {
|
||||||
|
|||||||
@@ -164,9 +164,9 @@ func renderEdges(n node) []renderEdge {
|
|||||||
// contributes every variant, since any of them may be the one drawn. checkPath has
|
// contributes every variant, since any of them may be the one drawn. checkPath has
|
||||||
// already proved the tail resolves in every variant.
|
// already proved the tail resolves in every variant.
|
||||||
func pathLeaves(n node, tail []string) []node {
|
func pathLeaves(n node, tail []string) []node {
|
||||||
var out []node
|
w := &proveWalk{tail: tail}
|
||||||
_, _ = walkPath(n, tail, pathWalk{mode: walkEvery, pins: &pinSet{}, leaf: func(n node) error { out = append(out, n); return nil }})
|
_, _ = w.walk(n, tail)
|
||||||
return out
|
return w.leaves
|
||||||
}
|
}
|
||||||
|
|
||||||
type renderCounts map[node]int
|
type renderCounts map[node]int
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw
|
|||||||
if len(a.tail) > 0 {
|
if len(a.tail) > 0 {
|
||||||
sc.pins = &d.pins
|
sc.pins = &d.pins
|
||||||
}
|
}
|
||||||
r := renderLeaf(s, drawPath(t.head(a.key), a.tail, a.key, &d.pins, &pathDraws{s: s, held: d, a: &a}), sc)
|
r := renderLeaf(s, drawPath(t.head(a.key), a.tail, a.key, &renderWalk{s: s, pins: &d.pins, held: d, a: &a}), sc)
|
||||||
if d.value == nil {
|
if d.value == nil {
|
||||||
d.value = map[string]draw{}
|
d.value = map[string]draw{}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -100,7 +100,7 @@ func operandReader(t *template, head string) string {
|
|||||||
// coverPath collects what holding one path pins: every choice level the path
|
// coverPath collects what holding one path pins: every choice level the path
|
||||||
// passes through, whole, and the leaf it renders.
|
// passes through, whole, and the leaf it renders.
|
||||||
func coverPath(n node, tail []string, into map[node]bool) {
|
func coverPath(n node, tail []string, into map[node]bool) {
|
||||||
_, _ = walkPath(n, tail, pathWalk{mode: walkCover, leaf: func(n node) error { cover(n, into, false); return nil }})
|
_, _ = (&coverWalk{into: into}).walk(n, tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cover collects a level and everything contained in it. A fixed string outside a
|
// cover collects a level and everything contained in it. A fixed string outside a
|
||||||
|
|||||||
@@ -125,154 +125,282 @@ func nameSegments(segs []string) []string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// walkMode is what a walk does at a choice, and whether it reads the rows a path
|
// stepInto is what seg names under n: a folder's entry or a template's field. A
|
||||||
// selects and draws.
|
// missing segment is an error, so no walk reaches past what the data holds.
|
||||||
type walkMode uint8
|
func stepInto(n node, seg string) (node, error) {
|
||||||
|
if isSelector(seg) {
|
||||||
const (
|
return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(seg))
|
||||||
// walkEvery proves every variant carries the rest of the path and walks each,
|
|
||||||
// pinning the rows it selects in pins.
|
|
||||||
walkEvery walkMode = iota
|
|
||||||
// walkCover stops at a choice, handing it whole to leaf, and reads no row.
|
|
||||||
walkCover
|
|
||||||
// walkProbe proves as walkEvery does, walking one variant, so a path that
|
|
||||||
// resolves resolves whichever variants and rows are drawn, and marks in drawn
|
|
||||||
// the tables a draw would pin.
|
|
||||||
walkProbe
|
|
||||||
// walkDraw draws the rows and variants a proved path reads; drawPath is its
|
|
||||||
// one entry.
|
|
||||||
walkDraw
|
|
||||||
)
|
|
||||||
|
|
||||||
// pathWalk is one walk of a dotted path: level runs at each template a segment
|
|
||||||
// descends into, leaf where the walk ends, and a nil action is skipped.
|
|
||||||
type pathWalk struct {
|
|
||||||
mode walkMode
|
|
||||||
level func(t *template, rest []string) error
|
|
||||||
leaf func(n node) error
|
|
||||||
pins *pinSet
|
|
||||||
drawn map[*table]bool
|
|
||||||
draws *pathDraws
|
|
||||||
}
|
|
||||||
|
|
||||||
// pathDraws is what a walk draws with: the session, and, for a held read, the hold
|
|
||||||
// keeping the variant drawn at each level of a, so paths sharing a prefix share it.
|
|
||||||
type pathDraws struct {
|
|
||||||
s *session
|
|
||||||
held *hold
|
|
||||||
a *arm
|
|
||||||
}
|
|
||||||
|
|
||||||
// walkPath descends tail from n and returns the node it ends at: a folder or
|
|
||||||
// template by its next segment, a choice by its mode, which consumes no segment, a
|
|
||||||
// table by walkTable. A missing segment is an error, so no walk reaches past what
|
|
||||||
// the data holds.
|
|
||||||
func walkPath(n node, tail []string, w pathWalk) (node, error) {
|
|
||||||
if len(tail) == 0 {
|
|
||||||
return n, w.atLeaf(n)
|
|
||||||
}
|
|
||||||
if t, isTable := n.(*table); isTable {
|
|
||||||
return walkTable(t, tail, w, false)
|
|
||||||
}
|
|
||||||
if isSelector(tail[0]) {
|
|
||||||
return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(tail[0]))
|
|
||||||
}
|
}
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *folder:
|
case *folder:
|
||||||
child, ok := n.children[tail[0]]
|
if child, ok := n.children[seg]; ok {
|
||||||
if !ok {
|
return child, nil
|
||||||
return nil, fmt.Errorf("no entry %q", tail[0])
|
|
||||||
}
|
}
|
||||||
return walkPath(child, tail[1:], w)
|
return nil, fmt.Errorf("no entry %q", seg)
|
||||||
case *template:
|
case *template:
|
||||||
if w.level != nil {
|
if child, ok := n.fields[seg]; ok {
|
||||||
if err := w.level(n, tail); err != nil {
|
return child, nil
|
||||||
|
}
|
||||||
|
case *tableColumn:
|
||||||
|
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", seg, n.t.header[n.i])
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no field %q", seg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tableRoute is how a path passes one table: the row it reads, by selector or
|
||||||
|
// drawn, and what it goes on into, a linked table it descends to among them.
|
||||||
|
type tableRoute struct {
|
||||||
|
sel string
|
||||||
|
draw bool
|
||||||
|
next node
|
||||||
|
rest []string
|
||||||
|
descends bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// route is how tail passes t, reached from another table where descended. The path
|
||||||
|
// reads a row wherever a segment follows or the table was reached from another; a
|
||||||
|
// table reached whole, with no selector, is left to a render's own draw.
|
||||||
|
func (t *table) route(tail []string, descended bool) (tableRoute, error) {
|
||||||
|
sel, tail, err := t.selector(tail)
|
||||||
|
if err != nil {
|
||||||
|
return tableRoute{}, err
|
||||||
|
}
|
||||||
|
column, child, err := t.step(tail)
|
||||||
|
if err != nil {
|
||||||
|
return tableRoute{}, err
|
||||||
|
}
|
||||||
|
// A selector further down pins this table by ancestry, so the walk draws only
|
||||||
|
// where none follows; drawing first could pick a row the selector is not inside.
|
||||||
|
r := tableRoute{sel: sel, draw: sel == "" && (descended || len(tail) > 0) && !hasSelector(tail)}
|
||||||
|
switch {
|
||||||
|
case len(tail) == 0 && sel == "" && !descended:
|
||||||
|
r.next = t
|
||||||
|
case len(tail) == 0:
|
||||||
|
r.next = t.pinnedRow
|
||||||
|
case child != nil:
|
||||||
|
r.next, r.rest, r.descends = child, tail[1:], true
|
||||||
|
default:
|
||||||
|
r.next, r.rest = column, tail[1:]
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// proveWalk proves a path resolves whichever way the draws go: every variant of a
|
||||||
|
// choice carries the rest of it, and is walked, a selector names a row inside the
|
||||||
|
// rows selected before it, and no level read carries a repeat or a drawGroup. It
|
||||||
|
// collects every leaf the path may render; level and tail name a refused level.
|
||||||
|
type proveWalk struct {
|
||||||
|
pins pinSet
|
||||||
|
level string
|
||||||
|
tail []string
|
||||||
|
leaves []node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *proveWalk) walk(n node, tail []string) (node, error) {
|
||||||
|
for descended := false; len(tail) > 0 || descended; {
|
||||||
|
var err error
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *choice:
|
||||||
|
return w.walkEvery(x, tail)
|
||||||
|
case *table:
|
||||||
|
var r tableRoute
|
||||||
|
if r, err = x.route(tail, descended); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.sel != "" {
|
||||||
|
if err := w.pins.selectRow(x, r.sel); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
child, ok := n.fields[tail[0]]
|
n, tail, descended = r.next, r.rest, r.descends
|
||||||
if !ok {
|
continue
|
||||||
return nil, fmt.Errorf("no field %q", tail[0])
|
case *template:
|
||||||
|
if err := w.enter(x, tail); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return walkPath(child, tail[1:], w)
|
|
||||||
case *choice:
|
|
||||||
return walkChoice(n, tail, w)
|
|
||||||
case *tableColumn:
|
|
||||||
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", tail[0], n.t.header[n.i])
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no field %q", tail[0])
|
if n, err = stepInto(n, tail[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tail = tail[1:]
|
||||||
|
}
|
||||||
|
w.leaves = append(w.leaves, n)
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w pathWalk) atLeaf(n node) error {
|
func (w *proveWalk) walkEvery(c *choice, tail []string) (node, error) {
|
||||||
if w.leaf != nil {
|
|
||||||
return w.leaf(n)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// drawPath draws the rows and variants a path reads, pinning them in pins. The path
|
|
||||||
// must be proved first, by a probe or by New's fences, so the walk cannot fail.
|
|
||||||
// head is what n is reached by, for the panic.
|
|
||||||
func drawPath(n node, tail []string, head string, pins *pinSet, draws *pathDraws) node {
|
|
||||||
leaf, err := walkPath(n, tail, pathWalk{mode: walkDraw, pins: pins, draws: draws})
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("fejkdata: %s: %v; the path should have been proved before it was drawn", join(head, joinSegments(tail)), err))
|
|
||||||
}
|
|
||||||
return leaf
|
|
||||||
}
|
|
||||||
|
|
||||||
func walkChoice(c *choice, tail []string, w pathWalk) (node, error) {
|
|
||||||
switch w.mode {
|
|
||||||
case walkCover:
|
|
||||||
return c, w.atLeaf(c)
|
|
||||||
case walkDraw:
|
|
||||||
return walkPath(w.draws.variant(c, tail), tail, w)
|
|
||||||
}
|
|
||||||
if err := carriedByAll(c, tail); err != nil {
|
if err := carriedByAll(c, tail); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if w.mode == walkProbe {
|
|
||||||
return walkPath(c.items[0], tail, w)
|
|
||||||
}
|
|
||||||
var last node
|
var last node
|
||||||
for _, item := range c.items {
|
for _, item := range c.items {
|
||||||
var err error
|
var err error
|
||||||
if last, err = walkPath(item, tail, w); err != nil {
|
if last, err = w.walk(item, tail); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return last, nil
|
return last, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// walkTable descends tail from a table: a selector first, then a column or a
|
func (w *proveWalk) enter(t *template, rest []string) error {
|
||||||
// linked table by name. The walk reads a row wherever a segment follows or the
|
name := join(w.level, strings.Join(w.tail[:len(w.tail)-len(rest)], "."))
|
||||||
// table was reached from another; a table reached whole, with no selector, is left
|
switch {
|
||||||
// to a render's own draw.
|
case t.repeat > 1:
|
||||||
func walkTable(t *table, tail []string, w pathWalk, descended bool) (node, error) {
|
return fmt.Errorf("the level %q carries a repeat, which a path reading one draw of it cannot apply", name)
|
||||||
sel, tail, err := t.selector(tail)
|
case t.drawGroup != "":
|
||||||
|
return fmt.Errorf("the level %q carries a drawGroup, which a path reading into it cannot apply", name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// coverWalk collects into what holding a path pins: every choice level it passes
|
||||||
|
// through, whole, and the leaf it renders. It stops at a choice and reads no row.
|
||||||
|
type coverWalk struct{ into map[node]bool }
|
||||||
|
|
||||||
|
func (w *coverWalk) walk(n node, tail []string) (node, error) {
|
||||||
|
for descended := false; len(tail) > 0 || descended; {
|
||||||
|
var err error
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *choice:
|
||||||
|
cover(x, w.into, false)
|
||||||
|
return x, nil
|
||||||
|
case *table:
|
||||||
|
var r tableRoute
|
||||||
|
if r, err = x.route(tail, descended); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n, tail, descended = r.next, r.rest, r.descends
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, err = stepInto(n, tail[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tail = tail[1:]
|
||||||
|
}
|
||||||
|
cover(n, w.into, false)
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// probeWalk proves a path as proveWalk does, walking one variant of a choice, so a
|
||||||
|
// path that resolves resolves whichever variants and rows are drawn. It draws
|
||||||
|
// nothing, marking in drawn the tables a draw would pin.
|
||||||
|
type probeWalk struct {
|
||||||
|
pins pinSet
|
||||||
|
drawn map[*table]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *probeWalk) walk(n node, tail []string) (node, error) {
|
||||||
|
for descended := false; len(tail) > 0 || descended; {
|
||||||
|
var err error
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *choice:
|
||||||
|
if err := carriedByAll(x, tail); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n = x.items[0]
|
||||||
|
continue
|
||||||
|
case *table:
|
||||||
|
var r tableRoute
|
||||||
|
if r, err = x.route(tail, descended); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := w.readRow(x, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n, tail, descended = r.next, r.rest, r.descends
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, err = stepInto(n, tail[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tail = tail[1:]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *probeWalk) readRow(t *table, r tableRoute) error {
|
||||||
|
if r.sel != "" {
|
||||||
|
return w.pins.selectRow(t, r.sel)
|
||||||
|
}
|
||||||
|
if !r.draw {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if w.drawn == nil {
|
||||||
|
w.drawn = map[*table]bool{}
|
||||||
|
}
|
||||||
|
for stop := w.pins.nearestPinned(t); t != stop; t = t.parentT {
|
||||||
|
w.drawn[t] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderWalk draws the rows and variants a proved path reads, pinning them in pins.
|
||||||
|
// For a held read, held keeps the variant drawn at each level of a, so paths
|
||||||
|
// sharing a prefix share it.
|
||||||
|
type renderWalk struct {
|
||||||
|
s *session
|
||||||
|
pins *pinSet
|
||||||
|
held *hold
|
||||||
|
a *arm
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawPath walks w over a path proved first, by a probe or by New's fences, so the
|
||||||
|
// walk cannot fail. head is what n is reached by, for the panic.
|
||||||
|
func drawPath(n node, tail []string, head string, w *renderWalk) node {
|
||||||
|
leaf, err := w.walk(n, tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
panic(fmt.Sprintf("fejkdata: %s: %v; the path should have been proved before it was drawn", join(head, joinSegments(tail)), err))
|
||||||
}
|
}
|
||||||
column, child, err := t.step(tail)
|
return leaf
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
// A selector further down pins this table by ancestry, so the walk draws only
|
|
||||||
// where none follows; drawing first could pick a row the selector is not inside.
|
func (w *renderWalk) walk(n node, tail []string) (node, error) {
|
||||||
if w.mode != walkCover {
|
for descended := false; len(tail) > 0 || descended; {
|
||||||
if err := readRow(w, t, sel, (descended || len(tail) > 0) && !hasSelector(tail)); err != nil {
|
var err error
|
||||||
|
switch x := n.(type) {
|
||||||
|
case *choice:
|
||||||
|
n = w.variant(x, tail)
|
||||||
|
continue
|
||||||
|
case *table:
|
||||||
|
var r tableRoute
|
||||||
|
if r, err = x.route(tail, descended); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case len(tail) == 0 && sel == "" && !descended:
|
case r.sel != "":
|
||||||
return walkPath(t, nil, w)
|
if err := w.pins.selectRow(x, r.sel); err != nil {
|
||||||
case len(tail) == 0:
|
return nil, err
|
||||||
return walkPath(t.pinnedRow, nil, w)
|
|
||||||
case child != nil:
|
|
||||||
return walkTable(child, tail[1:], w, true)
|
|
||||||
}
|
}
|
||||||
return walkPath(column, tail[1:], w)
|
case r.draw:
|
||||||
|
x.drawIn(w.s, w.pins)
|
||||||
|
}
|
||||||
|
n, tail, descended = r.next, r.rest, r.descends
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n, err = stepInto(n, tail[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tail = tail[1:]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// variant is the variant of c the walk continues into: the one held for this level
|
||||||
|
// of the read, drawn once, where the walk holds its draws; else one drawn afresh.
|
||||||
|
func (w *renderWalk) variant(c *choice, rest []string) node {
|
||||||
|
if w.held == nil {
|
||||||
|
return pick(w.s, c)
|
||||||
|
}
|
||||||
|
key := w.a.levels[len(w.a.tail)-len(rest)]
|
||||||
|
n, drew := w.held.variant[key]
|
||||||
|
if !drew {
|
||||||
|
n = resolveChoice(w.s, c)
|
||||||
|
if w.held.variant == nil {
|
||||||
|
w.held.variant = map[string]node{}
|
||||||
|
}
|
||||||
|
w.held.variant[key] = n
|
||||||
|
}
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// selector splits the selector a tail starts with from the rest of it.
|
// selector splits the selector a tail starts with from the rest of it.
|
||||||
@@ -301,41 +429,6 @@ func (t *table) step(tail []string) (column node, child *table, err error) {
|
|||||||
return nil, child, nil
|
return nil, child, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readRow pins the row a path reads of t: the one its selector names, or one drawn
|
|
||||||
// where the path reads into the table.
|
|
||||||
func readRow(w pathWalk, t *table, sel string, draw bool) error {
|
|
||||||
switch {
|
|
||||||
case sel != "":
|
|
||||||
return w.pins.selectRow(t, sel)
|
|
||||||
case !draw:
|
|
||||||
case w.mode == walkDraw:
|
|
||||||
t.drawIn(w.draws.s, w.pins)
|
|
||||||
case w.drawn != nil:
|
|
||||||
for stop := w.pins.nearestPinned(t); t != stop; t = t.parentT {
|
|
||||||
w.drawn[t] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// variant is the variant of c the walk continues into: the one held for this level
|
|
||||||
// of the read, drawn once, where the walk holds its draws; else one drawn afresh.
|
|
||||||
func (d *pathDraws) variant(c *choice, rest []string) node {
|
|
||||||
if d.held == nil {
|
|
||||||
return pick(d.s, c)
|
|
||||||
}
|
|
||||||
key := d.a.levels[len(d.a.tail)-len(rest)]
|
|
||||||
n, drew := d.held.variant[key]
|
|
||||||
if !drew {
|
|
||||||
n = resolveChoice(d.s, c)
|
|
||||||
if d.held.variant == nil {
|
|
||||||
d.held.variant = map[string]node{}
|
|
||||||
}
|
|
||||||
d.held.variant[key] = n
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// carriedByAll is the choice rule a path that must resolve on every call obeys:
|
// carriedByAll is the choice rule a path that must resolve on every call obeys:
|
||||||
// the rest of the tail must be one every variant carries.
|
// the rest of the tail must be one every variant carries.
|
||||||
func carriedByAll(c *choice, rest []string) error {
|
func carriedByAll(c *choice, rest []string) error {
|
||||||
@@ -360,26 +453,9 @@ func unreachableInChoice(c *choice, want string) error {
|
|||||||
return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered)
|
return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkPath proves a dotted tail resolves whichever way the draws go — a choice
|
// checkPath proves a dotted tail as proveWalk does, so a path that validates here
|
||||||
// must carry the rest of the path in the set every variant shares, a selector must
|
// resolves on every render, and a typo is a New-time error.
|
||||||
// name one row inside the rows selected before it — and that no level a path reads
|
|
||||||
// carries a repeat or a drawGroup, which one draw of it could not apply. So a path
|
|
||||||
// that validates here resolves on every render, and a typo is a New-time error.
|
|
||||||
func checkPath(n node, tail []string, level string) error {
|
func checkPath(n node, tail []string, level string) error {
|
||||||
var pins pinSet
|
_, err := (&proveWalk{level: level, tail: tail}).walk(n, tail)
|
||||||
_, err := walkPath(n, tail, pathWalk{
|
|
||||||
mode: walkEvery,
|
|
||||||
level: func(t *template, rest []string) error {
|
|
||||||
name := join(level, strings.Join(tail[:len(tail)-len(rest)], "."))
|
|
||||||
switch {
|
|
||||||
case t.repeat > 1:
|
|
||||||
return fmt.Errorf("the level %q carries a repeat, which a path reading one draw of it cannot apply", name)
|
|
||||||
case t.drawGroup != "":
|
|
||||||
return fmt.Errorf("the level %q carries a drawGroup, which a path reading into it cannot apply", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
pins: &pins,
|
|
||||||
})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -187,9 +187,7 @@ func eachTemplate(root map[string]node, fn func(folder []string, path string, t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolveCategory walks a dotted path through the folders to the category it
|
// resolveCategory walks a dotted path through the folders to the category it
|
||||||
// names, returning that head, the node, and the tail left to read into it. A
|
// names, returning that head, the node, and the tail left to read into it.
|
||||||
// descent of its own rather than a walkPath: it walks folders only and returns
|
|
||||||
// where they end, not a leaf.
|
|
||||||
func resolveCategory(root map[string]node, segments []string) (head []string, target node, tail []string, err error) {
|
func resolveCategory(root map[string]node, segments []string) (head []string, target node, tail []string, err error) {
|
||||||
var n node = &folder{children: root}
|
var n node = &folder{children: root}
|
||||||
i := 0
|
i := 0
|
||||||
|
|||||||
@@ -44,11 +44,10 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
// every call.
|
// every call.
|
||||||
func descend(s *session, root node, segments []string, sc renderScope) (node, error) {
|
func descend(s *session, root node, segments []string, sc renderScope) (node, error) {
|
||||||
// Walked once without drawing first, so a path that fails moves no seeded stream.
|
// Walked once without drawing first, so a path that fails moves no seeded stream.
|
||||||
var probe pinSet
|
if _, err := (&probeWalk{}).walk(root, segments); err != nil {
|
||||||
if _, err := walkPath(root, segments, pathWalk{mode: walkProbe, pins: &probe}); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return drawPath(root, segments, "", &sc.hold().pins, &pathDraws{s: s}), nil
|
return drawPath(root, segments, "", &renderWalk{s: s, pins: &sc.hold().pins}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// render evaluates a compiled node to a string. compile validates every node up
|
// render evaluates a compiled node to a string. compile validates every node up
|
||||||
|
|||||||
Reference in New Issue
Block a user