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
14 changed files with 42 additions and 42 deletions
Showing only changes of commit 29f3821a21 - Show all commits
+8 -8
View File
@@ -199,9 +199,9 @@ func inlineScope(n node, label string) nodeScope {
// binding is one scope's way through bind, the one load pipeline; its fields are where
// scopes differ.
type binding struct {
scope nodeScope
link func() error
referenceFence func() error
scope nodeScope
link func() error
scopeFence func() error
// Set by a caller that proves the columns against their Go types itself.
typedByGo bool
}
@@ -216,7 +216,7 @@ func treeBinding(root map[string]node) binding {
}
return linkRefs(root)
},
referenceFence: func() error { return checkNoCycles(root) },
scopeFence: func() error { return checkNoCycles(root) },
}
}
@@ -224,7 +224,7 @@ func (b binding) bind() error {
if err := b.link(); err != nil {
return err
}
if err := b.referenceFence(); err != nil {
if err := b.scopeFence(); err != nil {
return err
}
if !b.typedByGo {
@@ -232,13 +232,13 @@ func (b binding) bind() error {
return err
}
}
return checkRenders(b.scope)
return checkNodeFences(b.scope)
}
// checkRenders runs the per-node fences over a scope, each over the whole scope
// checkNodeFences runs the per-node fences over a scope, each over the whole scope
// before the next, so which of several broken nodes is reported does not depend on
// the walk. Its walks terminate only where nothing renders itself.
func checkRenders(s nodeScope) error {
func checkNodeFences(s nodeScope) error {
mem := renderCounts{}
if err := s(func(path string, n node) error { return repeatCheck(path, n, mem) }); err != nil {
return err
+4 -4
View File
@@ -105,7 +105,7 @@ func checkOwnFamily(t *template) error {
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 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.segment, own.segment)
}
return nil
}
@@ -252,13 +252,13 @@ func checkFamilyPair(a, b pathRead) error {
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...)
tail = append([]string{x.tr.headTable.segment}, 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))
x.at.route.spelled(x.a.spelling), drawn.segment, 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))
x.at.route.spelled(x.a.spelling), drawn.segment, y.at.route.spelled(y.a.spelling))
}
}
return nil
+6 -6
View File
@@ -64,14 +64,14 @@ type formatToken struct {
body string // the braces' content, as written
fn string // builtinCall
args []string // builtinCall
names []string // fieldAlternation: the '|' arms; builtinCall: the operands its builtin reads
names []string // nameRead: the '|' arms; builtinCall: the operands its builtin reads
}
type tokenKind uint8
const (
builtinCall tokenKind = iota + 1
fieldAlternation
nameRead
literalRun
)
@@ -93,7 +93,7 @@ func parseFormat(format string) ([]formatToken, error) {
}
flush()
if indexOutside(t.body, '(') < 0 {
toks = append(toks, formatToken{kind: fieldAlternation, body: t.body, names: splitOutside(t.body, '|')})
toks = append(toks, formatToken{kind: nameRead, body: t.body, names: splitOutside(t.body, '|')})
return nil
}
name, args, ok := funcCall(t.body)
@@ -220,7 +220,7 @@ func checkToken(t formatToken, fields map[string]node) error {
switch t.kind {
case builtinCall:
return checkFunc(t, fields)
case fieldAlternation:
case nameRead:
for _, name := range t.names {
if isRef(name) {
if _, _, err := refShape(name); err != nil {
@@ -372,7 +372,7 @@ type callFn func(s *session, emitted string, operands []string) string
// re-scans the format.
type op struct {
formatToken
arms []arm // fieldAlternation: the '|' alternatives, split into key and path once
arms []arm // nameRead: the '|' alternatives, split into key and path once
call callFn
// operands are the fields the builtin reads, in the order its operands func
// fixed; expand reads them before the call. nil for a builtin that reads none.
@@ -446,7 +446,7 @@ func compileOps(toks []formatToken, refs map[string]refBinding) formatOps {
c.ops = append(c.ops, op{formatToken: tok})
case builtinCall:
c.function(tok, refs)
case fieldAlternation:
case nameRead:
c.field(tok, refs)
}
}
+1 -1
View File
@@ -58,7 +58,7 @@ func (sc renderScope) at(n node, pins *pinSet) renderScope {
// rowOf is the row of t its columns render from.
func (sc renderScope) rowOf(t *table) int {
if sc.row.t != t {
panic(internalError("a column of %s renders in a scope holding no row of it", t.category))
panic(internalError("a column of %s renders in a scope holding no row of it", t.segment))
}
return sc.row.row
}
+1 -1
View File
@@ -151,7 +151,7 @@ func inlineBinding(n node, label string, root map[string]node) binding {
return binding{
scope: scope,
link: func() error { return linkNodeRefs(scope, root) },
referenceFence: func() error {
scopeFence: func() error {
if t, isTemplate := n.(*template); isTemplate && t.drawGroup != "" {
return fmt.Errorf("%s: drawGroup %q names nothing, since nothing can reference an inline template; drop it", label, t.drawGroup)
}
+3 -3
View File
@@ -176,7 +176,7 @@ func (t *table) route(tail []string, descended bool) (tableRoute, error) {
case len(tail) == 0 && sel == "" && !descended:
r.next = t
case len(tail) == 0:
r.next = t.pinnedRow
r.next = t.rowNode
case child != nil:
r.next, r.rest, r.descends = child, tail[1:], true
default:
@@ -415,7 +415,7 @@ func (t *table) selector(tail []string) (sel string, rest []string, err error) {
}
sel, rest = selectorOf(tail[0]), tail[1:]
if len(rest) > 0 && isSelector(rest[0]) {
return "", nil, fmt.Errorf("%s[%s] is selected twice; one selector names its row", t.category, sel)
return "", nil, fmt.Errorf("%s[%s] is selected twice; one selector names its row", t.segment, sel)
}
return sel, rest, nil
}
@@ -429,7 +429,7 @@ func (t *table) step(tail []string) (column node, child *table, err error) {
return t.fields[t.header[i]], nil, nil
}
if child = t.descendant(tail[0]); child == nil {
return nil, nil, fmt.Errorf("no column or linked table %q in %s", tail[0], t.category)
return nil, nil, fmt.Errorf("no column or linked table %q in %s", tail[0], t.segment)
}
return nil, child, nil
}
+1 -1
View File
@@ -32,7 +32,7 @@ func (p *pinSet) pinned(t *table) (int, bool) {
func (p *pinSet) mustRow(t *table) int {
r, ok := p.pinned(t)
if !ok {
panic(internalError("a column of %s is rendered with no row pinned", t.category))
panic(internalError("a column of %s is rendered with no row pinned", t.segment))
}
return r
}
+1 -1
View File
@@ -87,7 +87,7 @@ func renderRoot(s *session, t *template, trace renderTrace) {
set := holdSet{trace: trace}
sc := renderScope{set: &set}
sc.groupHold().pins.pin(tbl, r)
render(s, tbl.pinnedRow, sc.at(tbl.pinnedRow, &sc.groupHold().pins))
render(s, tbl.rowNode, sc.at(tbl.rowNode, &sc.groupHold().pins))
}
case t.repeat > 1:
expand(s, t, sc)
+1 -1
View File
@@ -264,7 +264,7 @@ func recordOf(n node) (*template, []Column, error) {
func renderRecord(s *session, t *template, columns []Column, sc renderScope) *Record {
sc = sc.in(t)
if t.site.row == formatRow {
sc = sc.at(t.site.table.pinnedRow, &sc.groupHold().pins)
sc = sc.at(t.site.table.rowNode, &sc.groupHold().pins)
}
r := &Record{columns: append([]Column(nil), columns...)}
for i := range r.columns {
+1 -1
View File
@@ -155,7 +155,7 @@ func columnReadOf(t *template, link templateLink) *columnRead {
// loneRef is the reference a format of one reference token and nothing else reads.
func loneRef(toks []formatToken) (string, bool) {
if len(toks) != 1 || toks[0].kind != fieldAlternation || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
if len(toks) != 1 || toks[0].kind != nameRead || len(toks[0].names) != 1 || !isRef(toks[0].names[0]) {
return "", false
}
return toks[0].names[0], true
+1 -1
View File
@@ -121,7 +121,7 @@ func expand(s *session, t *template, sc renderScope) string {
switch o.kind {
case literalRun:
b.WriteString(o.lit)
case fieldAlternation:
case nameRead:
b.WriteString(readField(s, t, held, sc, o.arms[s.IntN(len(o.arms))]).text)
case builtinCall:
// Read before the call, so the value a calc computes is the value the
+11 -11
View File
@@ -13,14 +13,14 @@ import (
// columns, each row is one draw, and the format renders the drawn row. A column is a
// cell of the row a render pinned; a cell carrying tokens compiles to a string node.
type table struct {
category string
segment string // path's last segment
path string // the category's path from the data root, which a selector is written at
file string
formatTemplate *template // fields are the column nodes
header []string
col map[string]int
fields map[string]node // column nodes, the format's fields
pinnedRow *tableRow
rowNode *tableRow
cells []string // rows × columns, flat
cellTemplates map[int]*template
keyIndex int // keyIndex through parentIndex are column indexes, -1 where the option is absent
@@ -84,7 +84,7 @@ func isTableOption(name string) bool {
const inSelector = `[]{}"|`
// compileTable compiles a category object naming a rows file into a table.
func compileTable(m map[string]any, category string, files *categoryFiles) (*table, error) {
func compileTable(m map[string]any, segment string, files *categoryFiles) (*table, error) {
o, err := readTableOptions(m)
if err != nil {
return nil, err
@@ -93,7 +93,7 @@ func compileTable(m map[string]any, category string, files *categoryFiles) (*tab
if err != nil {
return nil, err
}
t := &table{category: category, file: o.rows, keyIndex: -1, nameIndex: -1, weightIndex: -1, parentIndex: -1}
t := &table{segment: segment, file: o.rows, keyIndex: -1, nameIndex: -1, weightIndex: -1, parentIndex: -1}
if err := t.parseRows(data); err != nil {
return nil, fmt.Errorf("%s: %w", o.rows, err)
}
@@ -328,7 +328,7 @@ func (t *table) compileRowFormat(format string) error {
return err
}
for _, tok := range toks {
if tok.kind != fieldAlternation {
if tok.kind != nameRead {
continue
}
for _, name := range tok.names {
@@ -342,7 +342,7 @@ func (t *table) compileRowFormat(format string) error {
return err
}
t.formatTemplate = &template{format: format, tokens: toks, fields: t.fields, repeat: 1, isRecord: true, site: tableSite{t, formatRow}}
t.pinnedRow = &tableRow{t}
t.rowNode = &tableRow{t}
return nil
}
@@ -405,7 +405,7 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
var ancestors []*table
for q, seen := p, map[*table]bool{t: true}; q != nil; q, _ = siblings[q.header[q.parentIndex]].(*table) {
if seen[q] {
return fmt.Errorf("parent cycle: %s reaches itself through its parents", q.category)
return fmt.Errorf("parent cycle: %s reaches itself through its parents", q.segment)
}
seen[q] = true
ancestors = append(ancestors, q)
@@ -414,8 +414,8 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
}
}
for _, q := range ancestors {
if _, clash := q.col[t.category]; clash {
return fmt.Errorf("%q is named like a column of %q, its ancestor, so %s.%s could read either; rename one", t.category, q.category, q.category, t.category)
if _, clash := q.col[t.segment]; clash {
return fmt.Errorf("%q is named like a column of %q, its ancestor, so %s.%s could read either; rename one", t.segment, q.segment, q.segment, t.segment)
}
}
linked := make(map[string]bool, p.rowCount())
@@ -435,7 +435,7 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
if p.children == nil {
p.children = map[string]*table{}
}
p.children[t.category] = t
p.children[t.segment] = t
return nil
}
@@ -598,7 +598,7 @@ func (t *table) selectorSpelling(r int) string {
case t.keyIndex >= 0:
return t.path + "[" + t.cell(r, t.keyIndex) + "]"
case t.nameIndex >= 0:
return t.parentT.selectorSpelling(t.parentRow(r)) + "." + t.category + "[" + t.cell(r, t.nameIndex) + "]"
return t.parentT.selectorSpelling(t.parentRow(r)) + "." + t.segment + "[" + t.cell(r, t.nameIndex) + "]"
}
return fmt.Sprintf("%s line %d", t.file, r+2)
}
+1 -1
View File
@@ -1088,7 +1088,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
func categoriesOf(tables map[*table]bool) []string {
var out []string
for tbl := range tables {
out = append(out, tbl.category)
out = append(out, tbl.segment)
}
slices.Sort(out)
return out
+2 -2
View File
@@ -90,7 +90,7 @@ func (p *valueProof) prove(n node) proven {
case *tableColumn:
v = p.proveCells(n)
case *tableRow:
v = unproven(fmt.Sprintf("%q renders a row of %s, which is composed text", n.t.formatTemplate.format, n.t.category))
v = unproven(fmt.Sprintf("%q renders a row of %s, which is composed text", n.t.formatTemplate.format, n.t.segment))
default:
v = unproven(`it reads a null, which renders "" outside its own column`)
}
@@ -157,7 +157,7 @@ func (p *valueProof) proveTemplate(t *template) proven {
o := t.compiled.ops[0]
body, name, args := o.body, o.fn, o.args
switch _, isTransform := transforms[name]; {
case o.kind == fieldAlternation:
case o.kind == nameRead:
var leaves []node
for _, a := range o.arms {
leaves = append(leaves, pathLeaves(t.head(a.key), a.tail)...)