Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
19 changed files with 138 additions and 141 deletions
Showing only changes of commit d128621fc1 - Show all commits
+1 -1
View File
@@ -1332,7 +1332,7 @@ inline.go inline templates: Template, NewTemplate, FakeTemplate, IsTemplat
template.go the {token} grammar: scanning, tokens, operands, validation, compiling a format
hold.go the hold: one draw per expansion for paths and operands, and the rows it pins
holdfence.go the load fences over the hold: the routes and spellings that read one held name two ways
draw.go one reference draw per render and group: the draw 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: the drawGroup option, and the walk over a render's reads
family.go a family of linked tables: what a reference path reads of one, and the fence over paths into it
reference.go reference sigils, and binding references across the tree
+4 -4
View File
@@ -19,16 +19,16 @@ package fejkdata
// - draw — one pick from a choice, or one row taken from a table, and the value a
// read produced: `pick`, `drawn`, `table.draw`, `draw`.
// - expansion — one render of one format: `expand`.
// - render — what owns one `drawSet`, and so what one reference draw spans:
// - render — what owns one `holdSet`, and so what one reference draw spans:
// `Generator.Fake`, `FakeRecord`, `Template.Fake` and `RecordTemplate.Fake`
// start one each, `FakeStruct` one per record, and `expandAnew` one per
// iteration of a `template.repeat`.
// - hold — keeping one draw of a name, so every route to it reads that value:
// `template.held`, `draws`, `readField`.
// `template.held`, `hold`, `readField`.
// - draw group — reference draws held apart inside one render:
// `template.drawGroup` as the data spells it, `template.drawGroupKey` as a
// render reads it, `drawSet`, `drawScope`.
// - pin — fixing which row of a table the render uses: `draws.pin`, `draws.rowOf`.
// render reads it, `holdSet`, `renderScope`.
// - pin — fixing which row of a table the render uses: `hold.pin`, `hold.rowOf`.
// - family — a table and every table reaching it through a chain of
// `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
-57
View File
@@ -1,57 +0,0 @@
package fejkdata
import (
"strings"
)
// drawSet is one render's reference draws: the unnamed draw group's, and each named one's.
type drawSet struct {
unnamed draws
named map[string]*draws
}
// drawScope is where a render reads its reference paths: a draw set, in the draw group of the
// template rendering, and the row a table's format is rendering, which its columns read.
type drawScope struct {
set *drawSet
group string
t *table
row int
}
// newDrawSet makes the unnamed group's maps where the set is declared, keeping them on that frame's
// stack for a render that reads through them; a zero drawSet makes them on its first read instead.
func newDrawSet(s *session) drawSet {
return drawSet{unnamed: draws{variant: map[string]node{}, value: map[string]draw{}, s: s}}
}
// renderOnce renders n as one render, over draws of its own.
func renderOnce(s *session, n node) string {
set := drawSet{unnamed: draws{s: s}}
return render(s, n, drawScope{set: &set})
}
// in is the scope t renders in: its draw group where it names one, else its caller's.
func (sc drawScope) in(t *template) drawScope {
if t.drawGroupKey != "" {
sc.group = t.drawGroupKey
}
return sc
}
// draws is the set's draws for sc's draw group. The session is passed in rather than
// read out of the set: copying it from there would leak the set's maps to the heap.
func (sc drawScope) draws(s *session) *draws {
if sc.group == "" {
return &sc.set.unnamed
}
d, drew := sc.set.named[sc.group]
if !drew {
if sc.set.named == nil {
sc.set.named = map[string]*draws{}
}
d = &draws{variant: map[string]node{}, value: map[string]draw{}, s: s}
sc.set.named[strings.Clone(sc.group)] = d // a key from sc would leak sc, and with it every render's draw set, to the heap
}
return d
}
+8 -8
View File
@@ -235,8 +235,8 @@ func checkColumnDraws(t *template, columns []string) error {
// drawWalk gathers the references one render reads, by draw group, for check to compare.
type drawWalk struct {
reads []pathRead
read map[drawKey]bool
seen map[drawVisit]bool
read map[readKey]bool
seen map[nodeVisit]bool
}
// drawAt is where a walk stands: the draw group it draws in, how the render's root reached it, the
@@ -248,7 +248,7 @@ type drawAt struct {
drawn *table // left out of the visit keys: only this table's own cells compare against it, which the own-family fence keeps true
}
// rowSet stands in at load for the rows (*draws).pin holds at render: a path read enters a row with
// rowSet stands in at load for the rows (*hold).pin holds at render: a path read enters a row with
// its ancestors, a whole read enters the drawn row alone.
type rowSet []tablePin
@@ -311,26 +311,26 @@ type pathRead struct {
tr *tableRead // set where the reference names a table
}
type drawVisit struct {
type nodeVisit struct {
n node
group string
alt string
}
type drawKey struct {
type readKey struct {
group string
path string
alt string
}
func newDrawWalk() *drawWalk {
return &drawWalk{read: map[drawKey]bool{}, seen: map[drawVisit]bool{}}
return &drawWalk{read: map[readKey]bool{}, seen: map[nodeVisit]bool{}}
}
// walk follows what rendering n renders. A repeat renders over draws of its own, so the walk stops
// there.
func (w *drawWalk) walk(n node, at drawAt) {
v := drawVisit{n, at.group, at.alt.key()}
v := nodeVisit{n, at.group, at.alt.key()}
if w.seen[v] {
return
}
@@ -377,7 +377,7 @@ func (s rowSet) excludes(cell *template) bool {
func (w *drawWalk) edge(from node, e renderEdge, at drawAt) {
if a, reads := refRead(from, e.label); reads {
tr := tableReadOf(from.(*template).fields[a.key], a, e.to)
if k := (drawKey{at.group, a.path, at.alt.key()}); !w.read[k] {
if k := (readKey{at.group, a.path, at.alt.key()}); !w.read[k] {
w.read[k] = true
w.reads = append(w.reads, pathRead{at, a, tr})
}
+4 -4
View File
@@ -11,7 +11,7 @@ import (
// it lands on a row rendered whole.
type tableRead struct {
head *table
pins draws
pins hold
drawn map[*table]bool
sels []tableSel
whole bool
@@ -125,7 +125,7 @@ func replayPairs(reads []pathRead) error {
if o.tr == nil || o.at.group != r.at.group || alternatives(r.at, o.at) {
continue
}
var d draws
var d hold
if err := r.tr.replay(&d); err != nil {
return conflict(r, err)
}
@@ -142,7 +142,7 @@ func conflict(r pathRead, err error) error {
}
// replay pins the read's rows into d, where they agree with the rows pinned before.
func (r *tableRead) replay(d *draws) error {
func (r *tableRead) replay(d *hold) error {
var err error
r.pins.each(func(t *table, row int) {
if err == nil {
@@ -178,7 +178,7 @@ func checkFamilyPair(a, b pathRead) error {
}
// drawnOf is a table the read draws that pins holds a row of, if any.
func (r *tableRead) drawnOf(pins *draws) *table {
func (r *tableRead) drawnOf(pins *hold) *table {
var found *table
pins.each(func(t *table, _ int) {
if found == nil && r.drawn[t] {
+1 -1
View File
@@ -35,7 +35,7 @@ type Generator struct {
rand *session
categories map[string]node
root folder // the categories as the node a path walks from, owned here so a walk allocates none
set drawSet // one Fake's draws, owned here so a walk pinning rows keeps them off the heap
set holdSet // one Fake's hold set, owned here so a walk pinning rows keeps them off the heap
records map[node]recordShape
structs map[reflect.Type]structResult
}
+18 -18
View File
@@ -11,11 +11,11 @@ type tablePin struct {
row int
}
// draws is what has already been drawn for held names: the variant each was drawn as,
// hold is what has already been drawn for held names: the variant each was drawn as,
// so every path under it reads one row, and the draw each read made, by its one
// spelling, so the same read written twice reads one draw. An expansion keeps one for
// its sibling names, and a render one per group for its reference paths.
type draws struct {
type hold struct {
variant map[string]node
value map[string]draw
pins [8]tablePin // the rows pinned, inline so a render over a country's five-deep geo tree stays off the heap
@@ -31,7 +31,7 @@ type draw struct {
}
// pinned is the row the render pinned for t, if any.
func (d *draws) pinned(t *table) (int, bool) {
func (d *hold) pinned(t *table) (int, bool) {
for _, p := range d.pins[:d.npins] {
if p.t == t {
return p.row, true
@@ -42,7 +42,7 @@ func (d *draws) pinned(t *table) (int, bool) {
}
// mustRow is the row pinned for t, which the walk reaching a column pinned.
func (d *draws) mustRow(t *table) int {
func (d *hold) mustRow(t *table) int {
r, ok := d.pinned(t)
if !ok {
panic(fmt.Sprintf("fejkdata: a column of %s is rendered with no row pinned", t.category))
@@ -51,7 +51,7 @@ func (d *draws) mustRow(t *table) int {
}
// pin pins row r of t, and the rows of t's ancestors it links to.
func (d *draws) pin(t *table, r int) {
func (d *hold) pin(t *table, r int) {
for {
if _, done := d.pinned(t); done {
return
@@ -73,7 +73,7 @@ func (d *draws) pin(t *table, r int) {
}
// each calls fn for every pinned row, in pin order, the spilled ones by table name.
func (d *draws) each(fn func(t *table, r int)) {
func (d *hold) each(fn func(t *table, r int)) {
for _, p := range d.pins[:d.npins] {
fn(p.t, p.row)
}
@@ -90,7 +90,7 @@ func (d *draws) each(fn func(t *table, r int)) {
// rowOf is the render's row of t: the one pinned, 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 *draws) rowOf(t *table) int {
func (d *hold) rowOf(t *table) int {
if r, ok := d.pinned(t); ok {
return r
}
@@ -113,7 +113,7 @@ func (d *draws) rowOf(t *table) int {
}
// pinRow pins row r of t where it agrees with the rows pinned before it.
func (d *draws) pinRow(t *table, r int) error {
func (d *hold) pinRow(t *table, r int) error {
if pr, ok := d.pinned(t); ok && pr != r {
return fmt.Errorf("%s and %s are two rows of %s", t.selectorSpelling(pr), t.selectorSpelling(r), t.path)
}
@@ -127,7 +127,7 @@ func (d *draws) pinRow(t *table, r int) error {
}
// selectRow pins the row a selector names.
func (d *draws) selectRow(t *table, sel string) error {
func (d *hold) selectRow(t *table, sel string) error {
r, err := t.find(sel, d)
if err != nil {
return err
@@ -136,7 +136,7 @@ func (d *draws) selectRow(t *table, sel string) error {
}
// inside keeps the rows of t that sit inside every pinned ancestor.
func (d *draws) inside(t *table, rows []int) []int {
func (d *hold) inside(t *table, rows []int) []int {
for a := t.parentT; a != nil; a = a.parentT {
pa, ok := d.pinned(a)
if !ok {
@@ -160,14 +160,14 @@ func (d *draws) inside(t *table, rows []int) []int {
// gives one value, and a shown operand is the operand computed. Every other name is
// drawn afresh, so {word} {word} still draws twice. checkTokens, checkPath and
// linkRefs prove every step, so the walk cannot fail.
func readField(s *session, t *template, held *draws, sc drawScope, a arm) draw {
func readField(s *session, t *template, held *hold, sc renderScope, a arm) draw {
if !t.held[a.key] {
if len(a.tail) > 0 {
panic(fmt.Sprintf("fejkdata: %q reads a path into %q, which the expansion does not hold", a.name, a.key))
}
return draw{text: render(s, t.fields[a.key], sc)}
}
d := readScope(s, held, sc, a)
d := readHold(s, held, sc, a)
if r, done := d.value[a.path]; done {
return r
}
@@ -202,26 +202,26 @@ func readField(s *session, t *template, held *draws, sc drawScope, a arm) draw {
return r
}
// readScope is the draws a held read keeps its draw in: for a reference that reads a path,
// the render's draws for its group, so its draw spans the render; for a sibling, or a
// readHold is the hold a held read keeps its draw in: for a reference that reads a path,
// the render's hold for its group, so its draw spans the render; for a sibling, or a
// reference read whole, held.
func readScope(s *session, held *draws, sc drawScope, a arm) *draws {
func readHold(s *session, held *hold, sc renderScope, a arm) *hold {
if isRef(a.key) && len(a.tail) > 0 {
return sc.draws(s)
return sc.hold(s)
}
return held
}
// renderLeaf draws and renders what a read lands on: null on a null item, or on a column of one
// reference alone whose read drew null.
func renderLeaf(s *session, n node, sc drawScope) draw {
func renderLeaf(s *session, n node, sc renderScope) draw {
n = drawn(s, n)
if _, isNull := n.(*null); isNull {
return draw{null: true}
}
r := draw{text: render(s, n, sc)}
if t, _ := n.(*template); t != nil && t.readsColumn != nil {
r.null = sc.in(t).draws(s).value[t.readsColumn.a.path].null
r.null = sc.in(t).hold(s).value[t.readsColumn.a.path].null
}
return r
}
+1 -1
View File
@@ -766,6 +766,6 @@ func TestReadFieldPanicsOnAnUnheldPath(t *testing.T) {
t.Fatal("not a template")
}
mustPanic(t, "unheld arm with a path", func() {
readField(engine(1).rand, tm, nil, drawScope{}, arm{name: "w.x", key: "w", tail: []string{"x"}, path: "w.x"})
readField(engine(1).rand, tm, nil, renderScope{}, arm{name: "w.x", key: "w", tail: []string{"x"}, path: "w.x"})
})
}
+3 -3
View File
@@ -45,10 +45,10 @@ func heldHeads(t *template) []string {
return heads
}
// pinned collects what the hold of head answers for: a path pins the levels it
// heldNodes is what the hold of head answers for: a path pins the levels it
// passes through and the leaf it lands on, an operand exactly the value its render
// produces.
func pinned(t *template, head string, readers []reader) map[node]bool {
func heldNodes(t *template, head string, readers []reader) map[node]bool {
held := map[node]bool{}
if _, isPath := t.bound[head]; !isPath {
operandDraw(t.fields[head], held)
@@ -66,7 +66,7 @@ func pinned(t *template, head string, readers []reader) map[node]bool {
// holding it. One seen set across the edges: a node that cannot reach the level
// cannot reach it by another route either, so it is walked once.
func checkHeadHeld(t *template, head string, readers []reader) error {
held := pinned(t, head, readers)
held := heldNodes(t, head, readers)
if len(held) == 0 {
return nil // a fixed head holds nothing to reach
}
+57
View File
@@ -0,0 +1,57 @@
package fejkdata
import (
"strings"
)
// holdSet is one render's reference draws: the unnamed draw group's, and each named one's.
type holdSet struct {
unnamed hold
named map[string]*hold
}
// renderScope is where a render reads its reference paths: a hold set, in the draw group of the
// template rendering, and the row a table's format is rendering, which its columns read.
type renderScope struct {
set *holdSet
group string
t *table
row int
}
// newHoldSet makes the unnamed group's maps where the set is declared, keeping them on that frame's
// stack for a render that reads through them; a zero holdSet makes them on its first read instead.
func newHoldSet(s *session) holdSet {
return holdSet{unnamed: hold{variant: map[string]node{}, value: map[string]draw{}, s: s}}
}
// renderOnce renders n as one render, over draws of its own.
func renderOnce(s *session, n node) string {
set := holdSet{unnamed: hold{s: s}}
return render(s, n, renderScope{set: &set})
}
// in is the scope t renders in: its draw group where it names one, else its caller's.
func (sc renderScope) in(t *template) renderScope {
if t.drawGroupKey != "" {
sc.group = t.drawGroupKey
}
return sc
}
// hold is the set's hold for sc's draw group. The session is passed in rather than
// read out of the set: copying it from there would leak the set's maps to the heap.
func (sc renderScope) hold(s *session) *hold {
if sc.group == "" {
return &sc.set.unnamed
}
d, drew := sc.set.named[sc.group]
if !drew {
if sc.set.named == nil {
sc.set.named = map[string]*hold{}
}
d = &hold{variant: map[string]node{}, value: map[string]draw{}, s: s}
sc.set.named[strings.Clone(sc.group)] = d // a key from sc would leak sc, and with it every render's hold set, to the heap
}
return d
}
+1 -1
View File
@@ -47,7 +47,7 @@ func (*null) isNode() {}
// run: where the format holds a reference, that binds it and compiles the format
// again.
type template struct {
// Filled by `compileString`, `compileTemplate` and `table.compileFormat`:
// Filled by `compileString`, `compileTemplate` and `table.compileWhole`:
format string
fields map[string]node // `linkTemplateRefs` adds each bound reference here
repeat int
+3 -3
View File
@@ -135,7 +135,7 @@ type pathWalk struct {
choice func(c *choice, rest []string) ([]node, error)
level func(t *template, rest []string) error
leaf func(n node) error
pins *draws
pins *hold
}
// walkPath descends tail from n and returns the node it ends at: a folder or
@@ -272,7 +272,7 @@ 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
// walk draws, one drawn where the path reads into the table.
func readRow(d *draws, t *table, sel string, draw bool) error {
func readRow(d *hold, t *table, sel string, draw bool) error {
if sel != "" {
return d.selectRow(t, sel)
}
@@ -312,7 +312,7 @@ func unreachableInChoice(c *choice, want string) error {
// 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 {
var pins draws
var pins hold
_, err := walkPath(n, tail, pathWalk{
choice: func(c *choice, rest []string) ([]node, error) {
if err := carriedByAll(c, rest); err != nil {
+8 -8
View File
@@ -130,8 +130,8 @@ func (f *Generator) FakeRecord(path string) (*Record, error) {
if err != nil {
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
}
set := newDrawSet(f.rand)
sc := drawScope{set: &set}
set := newHoldSet(f.rand)
sc := renderScope{set: &set}
if t, isTable := n.(*table); isTable {
if n, err = tableRecord(f.rand, t, tail, sc); err != nil {
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
@@ -152,14 +152,14 @@ func (f *Generator) FakeRecord(path string) (*Record, error) {
// tableRecord walks a path's tail from a table to the table whose row is the record,
// pinning the rows it selects or draws.
func tableRecord(s *session, t *table, tail []string, sc drawScope) (node, error) {
func tableRecord(s *session, t *table, tail []string, sc renderScope) (node, error) {
n, err := descend(s, t, tail, sc)
if err != nil {
return nil, err
}
switch n := n.(type) {
case *table:
sc.draws(s).rowOf(n)
sc.hold(s).rowOf(n)
return n, nil
case *row:
return n.t, nil
@@ -204,8 +204,8 @@ type RecordTemplate struct {
func (t *RecordTemplate) Fake() *Record {
t.g.mu.Lock()
defer t.g.mu.Unlock()
set := newDrawSet(t.g.rand)
return renderRecord(t.g.rand, t.t, t.columns, drawScope{set: &set})
set := newHoldSet(t.g.rand)
return renderRecord(t.g.rand, t.t, t.columns, renderScope{set: &set})
}
// NewRecordTemplate compiles an inline record — a JSON object with a format and
@@ -262,10 +262,10 @@ func recordOf(n node) (*template, []Column, error) {
// renderRecord draws each column once, in the name order recordOf fixed, as one render
// over sc's draws; a table's columns read the row pinned there.
func renderRecord(s *session, t *template, columns []Column, sc drawScope) *Record {
func renderRecord(s *session, t *template, columns []Column, sc renderScope) *Record {
sc = sc.in(t)
if t.table != nil {
sc.t, sc.row = t.table, sc.draws(s).mustRow(t.table)
sc.t, sc.row = t.table, sc.hold(s).mustRow(t.table)
}
r := &Record{columns: append([]Column(nil), columns...)}
for i := range r.columns {
+15 -15
View File
@@ -23,8 +23,8 @@ func (f *Generator) Fake(path string) (string, error) {
if err != nil {
return "", fmt.Errorf("fejkdata: %w", err)
}
f.set = drawSet{unnamed: draws{s: f.rand}}
sc := drawScope{set: &f.set}
f.set = holdSet{unnamed: hold{s: f.rand}}
sc := renderScope{set: &f.set}
f.root.children = f.categories
n, err := descend(f.rand, &f.root, segments, sc)
if err != nil {
@@ -42,19 +42,19 @@ func (f *Generator) Fake(path string) (string, error) {
// choice consumes no segment, so the rest of the path must be one every variant
// carries before a variant is picked — a path that resolves at all resolves on
// every call.
func descend(s *session, root node, segments []string, sc drawScope) (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.
var probe draws
var probe hold
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.hold(s)})
}
// render evaluates a compiled node to a string. compile validates every node up
// front, so rendering a compiled tree cannot fail. sc holds the reference draws the
// render shares; each repeat iteration renders over draws of its own.
func render(s *session, n node, sc drawScope) string {
func render(s *session, n node, sc renderScope) string {
switch n := n.(type) {
case *choice:
return render(s, pick(s, n), sc)
@@ -64,11 +64,11 @@ func render(s *session, n node, sc drawScope) string {
sc.t, sc.row = n, n.draw(s)
return expand(s, n.format, sc)
case *row:
sc.t, sc.row = n.t, sc.draws(s).mustRow(n.t)
sc.t, sc.row = n.t, sc.hold(s).mustRow(n.t)
return expand(s, n.t.format, sc)
case *column:
if sc.t != n.t {
sc.t, sc.row = n.t, sc.draws(s).mustRow(n.t)
sc.t, sc.row = n.t, sc.hold(s).mustRow(n.t)
}
if cell := n.t.cellNode(sc.row, n.i); cell != nil {
return render(s, cell, sc)
@@ -97,19 +97,19 @@ func render(s *session, n node, sc drawScope) string {
}
// expandAnew expands one repeat iteration of t as a render of its own, in no group. Inlined into
// render's loop, its draw set would move to the heap.
// render's loop, its hold set would move to the heap.
//
//go:noinline
func expandAnew(s *session, t *template) string {
set := drawSet{unnamed: draws{s: s}}
return expand(s, t, drawScope{set: &set})
set := holdSet{unnamed: hold{s: s}}
return expand(s, t, renderScope{set: &set})
}
// pick selects one item. Uniform choices are O(1); weighted choices are an
// O(log n) search over precomputed cumulative weights. compile guarantees a
// non-empty choice and a finite positive total, so the index is always in range.
// The session is concrete rather than the rng interface, which would make the
// walk that draws through it leak its draw set to the heap.
// walk that draws through it leak its hold set to the heap.
func pick(s *session, c *choice) node {
if c.cum == nil {
return c.items[s.IntN(len(c.items))]
@@ -119,15 +119,15 @@ func pick(s *session, c *choice) node {
// expand renders a template's compiled ops. compile validated every token, so this
// cannot fail.
func expand(s *session, t *template, sc drawScope) string {
func expand(s *session, t *template, sc renderScope) string {
var b strings.Builder
b.Grow(t.grow)
// One draw per held name, for this expansion only: a nested template and each
// repeat iteration get their own, since each is its own expansion. A reference
// path reads the render's draws in sc instead.
var held *draws
var held *hold
if t.heldLocal {
held = &draws{
held = &hold{
variant: make(map[string]node, len(t.held)),
value: make(map[string]draw, len(t.held)),
}
+2 -2
View File
@@ -349,8 +349,8 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro
// fill draws the record into v's tagged fields, then each nested struct as a record of its own.
func (s *structShape) fill(sess *session, v reflect.Value) {
if s.record != nil {
set := newDrawSet(sess)
for i, c := range renderRecord(sess, s.record, s.columns, drawScope{set: &set}).columns {
set := newHoldSet(sess)
for i, c := range renderRecord(sess, s.record, s.columns, renderScope{set: &set}).columns {
setColumn(fieldAt(v, s.fields[i]), c)
}
}
+5 -4
View File
@@ -103,7 +103,7 @@ func compileTable(m map[string]any, category string, files *categoryFiles) (*tab
if err := t.checkCells(); err != nil {
return nil, fmt.Errorf("%s: %w", o.rows, err)
}
if err := t.compileFormat(o.format); err != nil {
if err := t.compileWhole(o.format); err != nil {
return nil, err
}
return t, nil
@@ -321,8 +321,9 @@ func (t *table) checkCells() error {
return nil
}
// compileFormat compiles the format over the columns as its fields.
func (t *table) compileFormat(format string) error {
// compileWhole compiles the format a row renders whole through, over the columns
// as its fields.
func (t *table) compileWhole(format string) error {
for _, name := range fieldTokens(format) {
a := splitArm(name, nil)
if _, ok := t.col[a.key]; !ok && !isRef(a.key) && a.key != "" {
@@ -524,7 +525,7 @@ func (t *table) under(r int, a *table, pr int) bool {
// find is the row a selector names: by key first, then by name, where a name
// naming several rows resolves inside the ancestors pinned in d.
func (t *table) find(sel string, d *draws) (int, error) {
func (t *table) find(sel string, d *hold) (int, error) {
if t.key < 0 && t.name < 0 {
return 0, fmt.Errorf("%s has no key or name column to select a row by", t.path)
}
+2 -2
View File
@@ -982,7 +982,7 @@ func TestRowSetSimulatesPinning(t *testing.T) {
t.Errorf("a drawn %s enters %v, want its own row alone", entered.selectorSpelling(row), drawn)
}
s := rowSet(nil).enter(entered, row, true)
var pinned draws
var pinned hold
held := 0
pinned.pin(entered, row)
pinned.each(func(tbl *table, r int) {
@@ -996,7 +996,7 @@ func TestRowSetSimulatesPinning(t *testing.T) {
}
for _, cand := range tables {
for cr := 0; cr < cand.rows(); cr++ {
var beside draws
var beside hold
beside.pin(entered, row)
refused := beside.pinRow(cand, cr) != nil
if got := s.excludes(&template{cellOf: cand, cellRow: cr}); got != refused {
+1 -1
View File
@@ -316,7 +316,7 @@ func TestGrowIsALowerBound(t *testing.T) {
t.Fatalf("format %q did not compile to a template", format)
}
for i := 0; i < 50; i++ {
if got := len(expand(f.rand, tmpl, drawScope{set: &drawSet{}})); got < tmpl.grow {
if got := len(expand(f.rand, tmpl, renderScope{set: &holdSet{}})); got < tmpl.grow {
t.Errorf("format %q: expand emitted %d bytes, below grow %d", format, got, tmpl.grow)
}
}
+4 -8
View File
@@ -21,14 +21,10 @@ name says whether a unit runs at `New` or at `Fake`.
This round, in order:
- Name the `draw` stem apart: the free `pinned` in `holdfence.go` is not
`(*draws).pinned` in `hold.go`, `compileFormat` is a method on both `*template` and
`*table`, and `draws`, `drawSet`, `drawScope`, `drawWalk`, `drawAt`, `drawKey`,
`drawVisit` and `drawRoute` no longer tell each other apart.
- Spell one fresh draw set one way: `newDrawSet` exists, and `render.go` and `draw.go`
hand-roll `drawSet{unnamed: draws{s: s}}` at three sites; name the lazy-map variant
if the difference is deliberate, and put the draw set in one file: `draw.go` holds 57
lines after the split, and `render.go` two of the three sites.
- Spell one fresh hold set one way: `newHoldSet` exists, and `render.go` and
`holdset.go` hand-roll `holdSet{unnamed: hold{s: s}}` at three sites; name the
lazy-map variant if the difference is deliberate, and put the hold set in one file:
`holdset.go` holds 57 lines after the split, and `render.go` two of the three sites.
- Run the package doc's Go example under test, as `readme_test.go` runs the README's
JSON blocks: `doc.go` and the README both print `Järvedsvägen 43\n891 77 Järved`
for seed 42, and nothing renders either, so a draw that moves leaves both false.