Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -29,7 +29,7 @@ package fejkdata
|
|||||||
// `template.drawGroup` as the data spells it, `template.drawGroupKey` as a
|
// `template.drawGroup` as the data spells it, `template.drawGroupKey` as a
|
||||||
// render reads it, `holdSet`, `renderScope`.
|
// render reads it, `holdSet`, `renderScope`.
|
||||||
// - pin — fixing which row of a table the render uses, which the draw fences replay:
|
// - pin — fixing which row of a table the render uses, which the draw fences replay:
|
||||||
// `pinSet`, `pinSet.pin`, `pinSet.rowOf`.
|
// `pinSet`, `pinSet.pin`, `pathDraws.row`.
|
||||||
// - family — a table and every table reaching it through a chain of
|
// - family — a table and every table reaching it through a chain of
|
||||||
// `table.parentT`, named by the root that chain ends at: `table.family`.
|
// `table.parentT`, named by the root that chain ends at: `table.family`.
|
||||||
// - whole — a read that lands on a `row`, so the row renders through its table's
|
// - whole — a read that lands on a `row`, so the row renders through its table's
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func tableReadOf(head node, a arm, leaf node) *tableRead {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
tr := &tableRead{head: t, drawn: map[*table]bool{}}
|
tr := &tableRead{head: t, drawn: map[*table]bool{}}
|
||||||
_, _ = walkPath(nil, t, a.tail, pathWalk{pins: &tr.pins})
|
_, _ = walkPath(t, a.tail, pathWalk{pins: &tr.pins})
|
||||||
written := a.name[:len(a.name)-len(joinSegments(a.tail))]
|
written := a.name[:len(a.name)-len(joinSegments(a.tail))]
|
||||||
cur := t
|
cur := t
|
||||||
tr.draws(cur)
|
tr.draws(cur)
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ func renderEdges(n node) []renderEdge {
|
|||||||
// 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
|
var out []node
|
||||||
_, _ = walkPath(nil, n, tail, pathWalk{
|
_, _ = walkPath(n, tail, pathWalk{
|
||||||
choice: func(c *choice, _ []string) ([]node, error) { return c.items, nil },
|
choice: func(c *choice, _ []string) ([]node, error) { return c.items, nil },
|
||||||
leaf: func(n node) error { out = append(out, n); return nil },
|
leaf: func(n node) error { out = append(out, n); return nil },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -38,26 +38,7 @@ func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw
|
|||||||
if r, done := d.value[a.path]; done {
|
if r, done := d.value[a.path]; done {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
leaf, err := walkPath(s, t.fields[a.key], a.tail, pathWalk{
|
leaf, err := walkPath(t.fields[a.key], a.tail, pathWalk{pins: &d.pins, draws: &pathDraws{s: s, held: d, a: &a}})
|
||||||
// Hold the draw at every level passed through, so two paths sharing a
|
|
||||||
// prefix share it.
|
|
||||||
choice: func(c *choice, rest []string) ([]node, error) {
|
|
||||||
key := a.key
|
|
||||||
if consumed := len(a.tail) - len(rest); consumed > 0 {
|
|
||||||
key = a.steps[consumed-1]
|
|
||||||
}
|
|
||||||
n, drew := d.variant[key]
|
|
||||||
if !drew {
|
|
||||||
n = drawn(s, c)
|
|
||||||
if d.variant == nil {
|
|
||||||
d.variant = map[string]node{}
|
|
||||||
}
|
|
||||||
d.variant[key] = n
|
|
||||||
}
|
|
||||||
return []node{n}, nil
|
|
||||||
},
|
|
||||||
pins: &d.pins,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("fejkdata: %q: %v; a fence should have refused this at New", a.name, err))
|
panic(fmt.Sprintf("fejkdata: %q: %v; a fence should have refused this at New", a.name, err))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -106,7 +106,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(nil, n, tail, pathWalk{
|
_, _ = walkPath(n, tail, pathWalk{
|
||||||
choice: func(c *choice, _ []string) ([]node, error) { cover(c, into, false); return nil, nil },
|
choice: func(c *choice, _ []string) ([]node, error) { cover(c, into, false); return nil, nil },
|
||||||
leaf: func(n node) error { cover(n, into, false); return nil },
|
leaf: func(n node) error { cover(n, into, false); return nil },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -127,29 +127,40 @@ func names(segs []string) []string {
|
|||||||
|
|
||||||
// pathWalk is what one walk of a dotted path does at each kind of level: choice
|
// pathWalk is what one walk of a dotted path does at each kind of level: choice
|
||||||
// returns the variants to continue into (none stops the walk), and where it is nil
|
// returns the variants to continue into (none stops the walk), and where it is nil
|
||||||
// the walk draws one with s, takes the first where s is nil, or stops where pins is
|
// the walk draws one with draws, takes the first where draws is nil, or stops where
|
||||||
// nil too; level runs at each template a segment descends
|
// pins is nil too; level runs at each template a segment descends
|
||||||
// into; leaf runs where the tail ends; pins is where the walk pins the table rows
|
// into; leaf runs where the tail ends; pins is where the walk pins the table rows
|
||||||
// it selects and draws, nil skipping tables. A nil action is skipped. A render's
|
// it selects and draws, nil skipping tables; draws is what draws the rows and
|
||||||
// walk sets only pins, so it allocates nothing.
|
// variants the path reads, nil proving instead that the path resolves whichever
|
||||||
|
// are drawn. A nil action is skipped. A render's walk sets only pins and draws, so
|
||||||
|
// it allocates nothing.
|
||||||
type pathWalk struct {
|
type pathWalk struct {
|
||||||
choice func(c *choice, rest []string) ([]node, error)
|
choice func(c *choice, rest []string) ([]node, error)
|
||||||
level func(t *template, rest []string) error
|
level func(t *template, rest []string) error
|
||||||
leaf func(n node) error
|
leaf func(n node) error
|
||||||
pins *pinSet
|
pins *pinSet
|
||||||
|
draws *pathDraws
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathDraws is what a walk draws with: the session, and, for a held read of a, the
|
||||||
|
// hold keeping the variant drawn at each level, so paths sharing a prefix share it.
|
||||||
|
// a is a pointer: held inline, its level keys take an eager hold set to the heap.
|
||||||
|
type pathDraws struct {
|
||||||
|
s *session
|
||||||
|
held *hold
|
||||||
|
a *arm
|
||||||
}
|
}
|
||||||
|
|
||||||
// walkPath descends tail from n and returns the node it ends at: a folder or
|
// walkPath descends tail from n and returns the node it ends at: a folder or
|
||||||
// template by its next segment, a choice by w.choice, which consumes no segment, a
|
// template by its next segment, a choice by w.choice, which consumes no segment, a
|
||||||
// table by walkTable. A missing segment is an error, so no walk reaches past what
|
// table by walkTable. A missing segment is an error, so no walk reaches past what
|
||||||
// the data holds. s draws rows and variants; where it is nil, only selectors pin.
|
// the data holds.
|
||||||
// s is a parameter: a pathWalk field beside pins moves the hold maps to the heap.
|
func walkPath(n node, tail []string, w pathWalk) (node, error) {
|
||||||
func walkPath(s *session, n node, tail []string, w pathWalk) (node, error) {
|
|
||||||
if len(tail) == 0 {
|
if len(tail) == 0 {
|
||||||
return n, w.atLeaf(n)
|
return n, w.atLeaf(n)
|
||||||
}
|
}
|
||||||
if t, isTable := n.(*table); isTable {
|
if t, isTable := n.(*table); isTable {
|
||||||
return walkTable(s, t, tail, w, false)
|
return walkTable(t, tail, w, false)
|
||||||
}
|
}
|
||||||
if isSelector(tail[0]) {
|
if isSelector(tail[0]) {
|
||||||
return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(tail[0]))
|
return nil, fmt.Errorf("%s is not a table, so it has no row to select", selectorOf(tail[0]))
|
||||||
@@ -160,7 +171,7 @@ func walkPath(s *session, n node, tail []string, w pathWalk) (node, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no entry %q", tail[0])
|
return nil, fmt.Errorf("no entry %q", tail[0])
|
||||||
}
|
}
|
||||||
return walkPath(s, child, tail[1:], w)
|
return walkPath(child, tail[1:], w)
|
||||||
case *template:
|
case *template:
|
||||||
if w.level != nil {
|
if w.level != nil {
|
||||||
if err := w.level(n, tail); err != nil {
|
if err := w.level(n, tail); err != nil {
|
||||||
@@ -171,9 +182,9 @@ func walkPath(s *session, n node, tail []string, w pathWalk) (node, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no field %q", tail[0])
|
return nil, fmt.Errorf("no field %q", tail[0])
|
||||||
}
|
}
|
||||||
return walkPath(s, child, tail[1:], w)
|
return walkPath(child, tail[1:], w)
|
||||||
case *choice:
|
case *choice:
|
||||||
return walkChoice(s, n, tail, w)
|
return walkChoice(n, tail, w)
|
||||||
case *column:
|
case *column:
|
||||||
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", tail[0], n.t.columns[n.i])
|
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", tail[0], n.t.columns[n.i])
|
||||||
}
|
}
|
||||||
@@ -187,20 +198,20 @@ func (w pathWalk) atLeaf(n node) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// walkChoice continues a walk into the variants w.choice returns, or into one drawn
|
// walkChoice continues a walk into the variants w.choice returns, or into one
|
||||||
// by s where the walk names no choice action.
|
// w.draws draws where the walk names no choice action.
|
||||||
func walkChoice(s *session, 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 {
|
if w.pins == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err := carriedByAll(c, tail); err != nil {
|
if w.draws == nil { // a probe: every variant carries the tail, so any one proves it
|
||||||
return nil, err
|
if err := carriedByAll(c, tail); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return walkPath(c.items[0], tail, w)
|
||||||
}
|
}
|
||||||
if s == nil { // a probe: every variant carries the tail, so any one proves it
|
return walkPath(w.draws.variant(c, tail), tail, w)
|
||||||
return walkPath(s, c.items[0], tail, w)
|
|
||||||
}
|
|
||||||
return walkPath(s, pick(s, c), tail, w)
|
|
||||||
}
|
}
|
||||||
next, err := w.choice(c, tail)
|
next, err := w.choice(c, tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -208,7 +219,7 @@ func walkChoice(s *session, c *choice, tail []string, w pathWalk) (node, error)
|
|||||||
}
|
}
|
||||||
var last node
|
var last node
|
||||||
for _, item := range next {
|
for _, item := range next {
|
||||||
if last, err = walkPath(s, item, tail, w); err != nil {
|
if last, err = walkPath(item, tail, w); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +230,7 @@ func walkChoice(s *session, c *choice, tail []string, w pathWalk) (node, error)
|
|||||||
// linked table by name. The walk reads a row wherever a segment follows or the
|
// linked table by name. The walk reads a row wherever a segment follows or the
|
||||||
// table was reached from another; a table reached whole, with no selector, is left
|
// table was reached from another; a table reached whole, with no selector, is left
|
||||||
// to a render's own draw.
|
// to a render's own draw.
|
||||||
func walkTable(s *session, t *table, tail []string, w pathWalk, descended bool) (node, error) {
|
func walkTable(t *table, tail []string, w pathWalk, descended bool) (node, error) {
|
||||||
sel, tail, err := t.selector(tail)
|
sel, tail, err := t.selector(tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -231,19 +242,19 @@ func walkTable(s *session, t *table, tail []string, w pathWalk, descended bool)
|
|||||||
// A selector further down pins this table by ancestry, so the walk draws only
|
// 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.
|
// where none follows; drawing first could pick a row the selector is not inside.
|
||||||
if w.pins != nil {
|
if w.pins != nil {
|
||||||
if err := readRow(s, w.pins, t, sel, (descended || len(tail) > 0) && !hasSelector(tail)); err != nil {
|
if err := readRow(w.pins, w.draws, t, sel, (descended || len(tail) > 0) && !hasSelector(tail)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case len(tail) == 0 && sel == "" && !descended:
|
case len(tail) == 0 && sel == "" && !descended:
|
||||||
return walkPath(s, t, nil, w)
|
return walkPath(t, nil, w)
|
||||||
case len(tail) == 0:
|
case len(tail) == 0:
|
||||||
return walkPath(s, t.whole, nil, w)
|
return walkPath(t.whole, nil, w)
|
||||||
case child != nil:
|
case child != nil:
|
||||||
return walkTable(s, child, tail[1:], w, true)
|
return walkTable(child, tail[1:], w, true)
|
||||||
}
|
}
|
||||||
return walkPath(s, column, tail[1:], w)
|
return walkPath(column, tail[1:], w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -274,16 +285,59 @@ func (t *table) step(tail []string) (column node, child *table, err error) {
|
|||||||
|
|
||||||
// readRow pins the row a path reads of t: the one its selector names, or, where the
|
// readRow pins the row a path reads of t: the one its selector names, or, where the
|
||||||
// walk draws, one drawn where the path reads into the table.
|
// walk draws, one drawn where the path reads into the table.
|
||||||
func readRow(s *session, pins *pinSet, t *table, sel string, draw bool) error {
|
func readRow(p *pinSet, draws *pathDraws, t *table, sel string, draw bool) error {
|
||||||
if sel != "" {
|
if sel != "" {
|
||||||
return pins.selectRow(t, sel)
|
return p.selectRow(t, sel)
|
||||||
}
|
}
|
||||||
if draw && s != nil {
|
if draw && draws != nil {
|
||||||
pins.rowOf(s, t)
|
draws.row(p, t)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// row is the render's row of t: the one pinned in p, 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 *pathDraws) row(p *pinSet, t *table) int {
|
||||||
|
if r, ok := p.pinned(t); ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
r := -1
|
||||||
|
for a := t.parentT; a != nil && r < 0; a = a.parentT {
|
||||||
|
if _, ok := p.pinned(a); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if t.parentT != a {
|
||||||
|
d.row(p, t.parentT)
|
||||||
|
}
|
||||||
|
pr, _ := p.pinned(t.parentT)
|
||||||
|
r = t.drawUnder(d.s, pr)
|
||||||
|
}
|
||||||
|
if r < 0 {
|
||||||
|
r = t.draw(d.s)
|
||||||
|
}
|
||||||
|
p.pin(t, r)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = drawn(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 {
|
||||||
@@ -315,7 +369,7 @@ func unreachableInChoice(c *choice, want string) error {
|
|||||||
// that validates here resolves on every render, and a typo is a New-time error.
|
// 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
|
var pins pinSet
|
||||||
_, err := walkPath(nil, n, tail, pathWalk{
|
_, err := walkPath(n, tail, pathWalk{
|
||||||
choice: func(c *choice, rest []string) ([]node, error) {
|
choice: func(c *choice, rest []string) ([]node, error) {
|
||||||
if err := carriedByAll(c, rest); err != nil {
|
if err := carriedByAll(c, rest); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+3
-3
@@ -13,14 +13,14 @@ func TestWalkPathStopsAtAMissingSegment(t *testing.T) {
|
|||||||
level: func(tm *template, rest []string) error { seen = append(seen, "level:"+rest[0]); return nil },
|
level: func(tm *template, rest []string) error { seen = append(seen, "level:"+rest[0]); return nil },
|
||||||
leaf: func(n node) error { seen = append(seen, "leaf"); return nil },
|
leaf: func(n node) error { seen = append(seen, "leaf"); return nil },
|
||||||
}
|
}
|
||||||
if _, err := walkPath(nil, n, []string{"a", "b"}, walk); err != nil {
|
if _, err := walkPath(n, []string{"a", "b"}, walk); err != nil {
|
||||||
t.Fatalf("walkPath(a.b) = %v", err)
|
t.Fatalf("walkPath(a.b) = %v", err)
|
||||||
}
|
}
|
||||||
if want := []string{"level:a", "level:b", "leaf"}; !slices.Equal(seen, want) {
|
if want := []string{"level:a", "level:b", "leaf"}; !slices.Equal(seen, want) {
|
||||||
t.Errorf("walk visited %v, want %v", seen, want)
|
t.Errorf("walk visited %v, want %v", seen, want)
|
||||||
}
|
}
|
||||||
seen = nil
|
seen = nil
|
||||||
_, err := walkPath(nil, n, []string{"a", "nope", "deeper"}, walk)
|
_, err := walkPath(n, []string{"a", "nope", "deeper"}, walk)
|
||||||
if err == nil || !strings.Contains(err.Error(), `no field "nope"`) {
|
if err == nil || !strings.Contains(err.Error(), `no field "nope"`) {
|
||||||
t.Errorf("walkPath(a.nope.deeper) = %v, want the missing segment named", err)
|
t.Errorf("walkPath(a.nope.deeper) = %v, want the missing segment named", err)
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ func TestWalkPathStopsAtAMissingSegment(t *testing.T) {
|
|||||||
func TestWalkPathChoiceConsumesNoSegment(t *testing.T) {
|
func TestWalkPathChoiceConsumesNoSegment(t *testing.T) {
|
||||||
n := compiled(t, `[{"format":"{f}","f":"1"},{"format":"{f}","f":"2"}]`)
|
n := compiled(t, `[{"format":"{f}","f":"1"},{"format":"{f}","f":"2"}]`)
|
||||||
var leaves []node
|
var leaves []node
|
||||||
_, err := walkPath(nil, n, []string{"f"}, pathWalk{
|
_, err := walkPath(n, []string{"f"}, pathWalk{
|
||||||
choice: func(c *choice, rest []string) ([]node, error) {
|
choice: func(c *choice, rest []string) ([]node, error) {
|
||||||
if len(rest) != 1 || rest[0] != "f" {
|
if len(rest) != 1 || rest[0] != "f" {
|
||||||
t.Errorf("choice saw rest %v, want [f]", rest)
|
t.Errorf("choice saw rest %v, want [f]", rest)
|
||||||
|
|||||||
@@ -81,31 +81,6 @@ func (p *pinSet) each(fn func(t *table, r int)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rowOf is the row of t: the one pinned, else one s draws 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 (p *pinSet) rowOf(s *session, t *table) int {
|
|
||||||
if r, ok := p.pinned(t); ok {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
r := -1
|
|
||||||
for a := t.parentT; a != nil && r < 0; a = a.parentT {
|
|
||||||
if _, ok := p.pinned(a); !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if t.parentT != a {
|
|
||||||
p.rowOf(s, t.parentT)
|
|
||||||
}
|
|
||||||
pr, _ := p.pinned(t.parentT)
|
|
||||||
r = t.drawUnder(s, pr)
|
|
||||||
}
|
|
||||||
if r < 0 {
|
|
||||||
r = t.draw(s)
|
|
||||||
}
|
|
||||||
p.pin(t, r)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// clash is the table whose pinned row keeps row r of t out: t itself pinned to another row, or the
|
// clash is the table whose pinned row keeps row r of t out: t itself pinned to another row, or the
|
||||||
// nearest ancestor pinned to a row r is not inside; nil where none does.
|
// nearest ancestor pinned to a row r is not inside; nil where none does.
|
||||||
func (p *pinSet) clash(t *table, r int) *table {
|
func (p *pinSet) clash(t *table, r int) *table {
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ func tableRecord(s *session, t *table, tail []string, sc renderScope) (node, err
|
|||||||
}
|
}
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *table:
|
case *table:
|
||||||
sc.hold().pins.rowOf(s, n)
|
(&pathDraws{s: s}).row(&sc.hold().pins, n)
|
||||||
return n, nil
|
return n, nil
|
||||||
case *row:
|
case *row:
|
||||||
return n.t, nil
|
return n.t, nil
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
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
|
var probe pinSet
|
||||||
if _, err := walkPath(nil, root, segments, pathWalk{pins: &probe}); err != nil {
|
if _, err := walkPath(root, segments, pathWalk{pins: &probe}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return walkPath(s, root, segments, pathWalk{pins: &sc.hold().pins})
|
return walkPath(root, segments, pathWalk{pins: &sc.hold().pins, draws: &pathDraws{s: s}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
+9
-9
@@ -273,11 +273,11 @@ func fieldTokens(format string) []string {
|
|||||||
// tail is what makes the arm a bound draw: its head is drawn once per expansion
|
// tail is what makes the arm a bound draw: its head is drawn once per expansion
|
||||||
// (see compileOps).
|
// (see compileOps).
|
||||||
type arm struct {
|
type arm struct {
|
||||||
name string // as written, for messages
|
name string // as written, for messages
|
||||||
key string
|
key string
|
||||||
tail []string
|
tail []string
|
||||||
steps []string // key per level passed through; the head and leaf hold their own
|
levels []string // the key at each level the tail passes through, the head's first
|
||||||
path string // key and tail, the one spelling every way of writing this read shares
|
path string // key and tail, the one spelling every way of writing this read shares
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitArm splits one name into key and tail. refs maps a reference to what
|
// splitArm splits one name into key and tail. refs maps a reference to what
|
||||||
@@ -302,11 +302,11 @@ func splitArm(name string, refs map[string]refBinding) arm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pathArm(name, key string, segs []string) arm {
|
func pathArm(name, key string, segs []string) arm {
|
||||||
var steps []string
|
levels := []string{key}
|
||||||
for i := 0; i < len(segs)-1; i++ { // every level except the leaf's own
|
for i := 0; i < len(segs)-1; i++ {
|
||||||
steps = append(steps, key+"."+strings.Join(segs[:i+1], "."))
|
levels = append(levels, key+"."+strings.Join(segs[:i+1], "."))
|
||||||
}
|
}
|
||||||
return arm{name: name, key: key, tail: segs, steps: steps, path: key + "." + strings.Join(segs, ".")}
|
return arm{name: name, key: key, tail: segs, levels: levels, path: key + "." + strings.Join(segs, ".")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitArms splits a token body's '|' alternatives.
|
// splitArms splits a token body's '|' alternatives.
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ This round, in order:
|
|||||||
`operandReader`, `checkNoRepeatedRead` and `compileOps`, and function-versus-field is
|
`operandReader`, `checkNoRepeatedRead` and `compileOps`, and function-versus-field is
|
||||||
decided by `indexOutside(body, '(')` in two of them and by `funcCall` in the rest.
|
decided by `indexOutside(body, '(')` in two of them and by `funcCall` in the rest.
|
||||||
- Name `pathWalk`'s modes: render, probe, check and cover are chosen today by which
|
- Name `pathWalk`'s modes: render, probe, check and cover are chosen today by which
|
||||||
callbacks are nil and whether `walkPath`'s session is, and `walkChoice` returns
|
callbacks are nil and whether `draws` is, and `walkChoice` returns `nil, nil` where
|
||||||
`nil, nil` where none is set.
|
none is set.
|
||||||
- Have the check mode report the tables a read draws, so `tableRead.drawn` and
|
- Have the check mode report the tables a read draws, so `tableRead.drawn` and
|
||||||
`entered`'s `pinsAncestors` flag stop restating `walkTable`'s draw condition and
|
`entered`'s `pinsAncestors` flag stop restating `walkTable`'s draw condition and
|
||||||
`rowOf`'s ancestor rule: a change to how a render draws rows still leaves the fence
|
`pathDraws.row`'s ancestor rule: a change to how a render draws rows still leaves the
|
||||||
proving the old rule. Hold a row rendered whole in `drawAt` beside `drawn`, so
|
fence proving the old rule. Hold a row rendered whole in `drawAt` beside `drawn`, so
|
||||||
`pinSet` holds only what `pin` pins and `alternatives` needs no exception. Settle a
|
`pinSet` holds only what `pin` pins and `alternatives` needs no exception. Settle a
|
||||||
cell of a municipality drawn whole beside a path pinning a region it is not in. Needs
|
cell of a municipality drawn whole beside a path pinning a region it is not in. Needs
|
||||||
the item above.
|
the item above.
|
||||||
|
|||||||
Reference in New Issue
Block a user