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
3 changed files with 21 additions and 17 deletions
Showing only changes of commit 32124c5b1e - Show all commits
+1 -1
View File
@@ -15,7 +15,7 @@ replacement, and each removed path, column or flag.
`repeat` binds picks again on every iteration. Refused at `New`: a name bound twice `repeat` binds picks again on every iteration. Refused at `New`: a name bound twice
outside any `repeat`, twice in one `repeat`, or both inside a `repeat` and outside it; a outside any `repeat`, twice in one `repeat`, or both inside a `repeat` and outside it; a
name that is a field or an option; a binding of anything but a reference; a binding in a name that is a field or an option; a binding of anything but a reference; a binding in a
choice's item; a binding nothing reads, or one read once whole, which the reference spells; a choice's item; a binding nothing reads, or one of a whole category read once whole, which the bare reference spells; a
path through a name that selects a row; a read of a name rendering a field twice where a path through a name that selects a row; a read of a name rendering a field twice where a
path through the name reads it; reads of a name in two draw groups, or inside a path through the name reads it; reads of a name in two draw groups, or inside a
`repeat`, where what it names reads a reference path; and a binding in a table. A field, column, `repeat`, where what it names reads a reference path; and a binding in a table. A field, column,
+2 -2
View File
@@ -731,8 +731,8 @@ Rejected at load, each error naming what to write instead:
- a name that is a field or an option; - a name that is a field or an option;
- a binding of anything but a reference; - a binding of anything but a reference;
- a binding in a choice's item, which every other item would leave unbound; - a binding in a choice's item, which every other item would leave unbound;
- a binding nothing reads, or read once whole outside a `repeat`, which the reference - a binding nothing reads, or a binding of a whole category read once whole where it is
spells: `{/word as w}{w}` is `{/word}`; bound, which the bare reference spells: `{/word as w}{w}` is `{/word}`;
- a path through a name that selects a row; - a path through a name that selects a row;
- a read of a name rendering a field twice, where a path through the name reads it; - a read of a name rendering a field twice, where a path through the name reads it;
- reads of a name in two draw groups, or inside a `repeat`, where what it names reads a - reads of a name in two draw groups, or inside a `repeat`, where what it names reads a
+18 -14
View File
@@ -3,6 +3,7 @@ package fejkdata
import ( import (
"fmt" "fmt"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@@ -130,21 +131,13 @@ func (sc *nameScope) spelled() string {
return "in one repeat" return "in one repeat"
} }
// settle refuses a binding read by nothing, or read once whole, outside a repeat, where its // settle refuses a binding read by nothing, and hands a scope binding names to its owner, which
// reference spells the same pick; and hands a scope binding names to its owner, which renders a // renders a frame of it.
// frame of it.
func (sc *nameScope) settle() error { func (sc *nameScope) settle() error {
for _, b := range sc.order { for _, b := range sc.order {
if len(b.uses) == 0 { if len(b.uses) == 0 {
return fmt.Errorf("%stoken {%s}: nothing reads name %q; drop the token", b.where, b.body, b.name) return fmt.Errorf("%stoken {%s}: nothing reads name %q; drop the token", b.where, b.body, b.name)
} }
if r := b.uses[0]; len(b.uses) == 1 && r.tail == "" && !r.nested {
spelling := b.ref
if !r.operand {
spelling = "{" + spelling + "}"
}
return fmt.Errorf("%stoken {%s}: name %q is read once, so it keeps nothing; write %s where it is read, and drop the token", b.where, b.body, b.name, spelling)
}
} }
if t, isTemplate := sc.owner.(*template); isTemplate && len(sc.order) > 0 { if t, isTemplate := sc.owner.(*template); isTemplate && len(sc.order) > 0 {
t.ownNameScope = sc t.ownNameScope = sc
@@ -207,9 +200,9 @@ func unresolved(where string, u unboundRead, seen *nameScope, scopes []*nameScop
if b, ok := sc.bindings[u.head]; ok { if b, ok := sc.bindings[u.head]; ok {
at := strings.TrimSuffix(b.where, ": ") at := strings.TrimSuffix(b.where, ": ")
if at == "" { if at == "" {
at = "the root template" at = "an item of the root choice"
} }
return fmt.Errorf("%s%w; name %q is bound inside the repeat at %s, which a read outside the repeat cannot see; bind it outside the repeat to read one pick on every line", where, u.err, u.head, at) return fmt.Errorf("%s%w; name %q is bound at %s, inside a repeat, which a read outside the repeat cannot see; bind it outside the repeat to read one pick on every line", where, u.err, u.head, at)
} }
} }
var visible []string var visible []string
@@ -220,7 +213,10 @@ func unresolved(where string, u unboundRead, seen *nameScope, scopes []*nameScop
} }
if len(visible) > 0 { if len(visible) > 0 {
sort.Strings(visible) sort.Strings(visible)
return fmt.Errorf("%stoken {%s}: no field or name %q; the names bound here are %v", where, u.body, u.head, visible) for i, name := range visible {
visible[i] = strconv.Quote(name)
}
return fmt.Errorf("%stoken {%s}: no field or name %q; the names bound here are %s", where, u.body, u.head, strings.Join(visible, ", "))
} }
return fmt.Errorf("%s%w", where, u.err) return fmt.Errorf("%s%w", where, u.err)
} }
@@ -319,10 +315,18 @@ func checkNameReads(path string, t *template) error {
}) })
} }
// checkUses refuses reads of b in two draw groups, or inside a repeat, where what b names reads a // checkUses refuses a binding of a category read once whole, which the bare reference spells, and
// reads of b in two draw groups, or inside a repeat, where what b names reads a
// reference path: that path is held per draw group and per iteration, so what the pick kept and // reference path: that path is held per draw group and per iteration, so what the pick kept and
// what it renders afresh would come from two draws of it. todo.md item 4 ends the hold. // what it renders afresh would come from two draws of it. todo.md item 4 ends the hold.
func (b *nameBinding) checkUses() error { func (b *nameBinding) checkUses() error {
if r := b.uses[0]; len(b.uses) == 1 && r.tail == "" && !r.nested && len(b.tail) == 0 {
spelling := b.ref
if !r.operand {
spelling = "{" + spelling + "}"
}
return fmt.Errorf("name %q is read once, whole, which the bare reference draws the same way; write %s where it is read, and drop the token", b.name, spelling)
}
if !readsHeld(b.head, map[node]bool{}) { if !readsHeld(b.head, map[node]bool{}) {
return nil return nil
} }