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
5 changed files with 29 additions and 25 deletions
Showing only changes of commit 317486df0a - Show all commits
+3 -3
View File
@@ -188,12 +188,12 @@ func ibanValid(s string) bool {
return rem == 1 return rem == 1
} }
// TestRegistryShapes pins the builtin contract compileOps relies on: every entry // TestRegistryShapes pins the builtin contract compileFormat relies on: every entry
// supplies prep, and args parsed at compile only behind a check. // supplies prep, and args parsed at compile only behind a check.
func TestRegistryShapes(t *testing.T) { func TestRegistryShapes(t *testing.T) {
for name, b := range builtins { for name, b := range builtins {
if b.prep == nil { if b.prep == nil {
t.Errorf("builtin %q has no prep: compileOps would call a nil func", name) t.Errorf("builtin %q has no prep: compileFormat would call a nil func", name)
} }
if b.arity != 0 && b.checkArgs == nil { if b.arity != 0 && b.checkArgs == nil {
t.Errorf("builtin %q parses args in prep with no check", name) t.Errorf("builtin %q parses args in prep with no check", name)
@@ -203,7 +203,7 @@ func TestRegistryShapes(t *testing.T) {
// TestArgGuardsPanic pins the guards that report a builtin arg its check should // TestArgGuardsPanic pins the guards that report a builtin arg its check should
// have rejected. No data reaches them — checkFunc runs a builtin's check before // have rejected. No data reaches them — checkFunc runs a builtin's check before
// compileOps ever calls prep — so they are exercised directly. // compileFormat ever calls prep — so they are exercised directly.
func TestArgGuardsPanic(t *testing.T) { func TestArgGuardsPanic(t *testing.T) {
for name, call := range map[string]func(){ for name, call := range map[string]func(){
"atoi on an unvalidated arg": func() { atoi("nope") }, "atoi on an unvalidated arg": func() { atoi("nope") },
+2 -12
View File
@@ -441,19 +441,9 @@ func (c *formatOps) field(tok formatToken, refs map[string]refBinding) {
c.ops = append(c.ops, op{formatToken: tok, arms: arms}) c.ops = append(c.ops, op{formatToken: tok, arms: arms})
} }
// compileFormat compiles a format into ops, and applies the fences that need the // compileFormat compiles a parsed format. Call checkTokens first: it is what proves
// compiled reads.
func compileFormat(toks []formatToken, refs map[string]refBinding) (formatOps, error) {
c := compileOps(toks, refs)
if err := checkNoOverlap(c); err != nil {
return c, err
}
return c, checkNoRepeatedRead(c)
}
// compileOps compiles a parsed format. Call checkTokens first: it is what proves
// every token valid. // every token valid.
func compileOps(toks []formatToken, refs map[string]refBinding) formatOps { func compileFormat(toks []formatToken, refs map[string]refBinding) formatOps {
var c formatOps var c formatOps
for _, tok := range toks { for _, tok := range toks {
switch tok.kind { switch tok.kind {
+14 -1
View File
@@ -15,10 +15,23 @@ func heldCheck(path string, n node) error {
if !ok || len(t.compiled.held) == 0 { if !ok || len(t.compiled.held) == 0 {
return nil return nil
} }
if err := checkHeld(t); err != nil {
return fmt.Errorf("%s: %w", t.site.label(path), err)
}
return nil
}
func checkHeld(t *template) error {
if err := checkNoOverlap(t.compiled); err != nil {
return err
}
if err := checkNoRepeatedRead(t.compiled); err != nil {
return err
}
readers := pathHeadReaders(t.compiled) readers := pathHeadReaders(t.compiled)
for _, name := range heldNames(t) { for _, name := range heldNames(t) {
if err := checkNameHeld(t, name, readers); err != nil { if err := checkNameHeld(t, name, readers); err != nil {
return fmt.Errorf("%s: %w", path, err) return err
} }
} }
return nil return nil
+8
View File
@@ -73,6 +73,14 @@ func (s tableSite) isCell() bool {
return s.table != nil && s.row != formatRow return s.table != nil && s.row != formatRow
} }
// label names the template at path in an error, a cell by its line in the rows file.
func (s tableSite) label(path string) string {
if s.isCell() {
return fmt.Sprintf("%s, line %d", path, s.row+2)
}
return path
}
// templateLink is what a template resolves to in the assembled tree. // templateLink is what a template resolves to in the assembled tree.
type templateLink struct { type templateLink struct {
refs map[string]refBinding // each reference the format reads -> what it resolves to refs map[string]refBinding // each reference the format reads -> what it resolves to
+2 -9
View File
@@ -81,18 +81,11 @@ func linkRefs(root map[string]node) error {
// linkTemplate resolves t's references in category, "" for an inline template, and // linkTemplate resolves t's references in category, "" for an inline template, and
// compiles its format against them. // compiles its format against them.
func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error { func linkTemplate(folder []string, path, category string, t *template, root map[string]node) error {
if t.site.isCell() { link, err := t.resolveLink(folder, t.site.label(path), category, root)
path = fmt.Sprintf("%s, line %d", path, t.site.row+2)
}
link, err := t.resolveLink(folder, path, category, root)
if err != nil { if err != nil {
return err return err
} }
compiled, err := compileFormat(t.tokens, link.refs) t.link, t.compiled = link, compileFormat(t.tokens, link.refs)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
t.link, t.compiled = link, compiled
compileArms(t) compileArms(t)
return nil return nil
} }