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
6 changed files with 83 additions and 81 deletions
Showing only changes of commit 097fae4cd2 - Show all commits
+4 -2
View File
@@ -960,14 +960,15 @@ Where the project is heading; the sections before Audience document what ships t
``` ```
doc.go the package doc, and the vocabulary the package is written in doc.go the package doc, and the vocabulary the package is written in
fejkdata.go Generator, New, options, List fejkdata.go Generator, New, options, List
list.go the paths List advertises, every one a node reaches a direct descent at a time
shipped.go the embedded data set, and loading a shipped category on the first call reaching it shipped.go the embedded data set, and loading a shipped category on the first call reaching it
shippedindex.go the index of shipped categories, generated from data/ shippedindex.go the index of shipped categories, generated from data/
node.go the node model and JSON -> node compilation node.go the node model and JSON -> node compilation
table.go tables: their options, format and cells, the link to the parent beside them, and how a path passes and draws a table table.go tables: their options, format and cells, the link to the parent beside them, and how a path passes and draws a table
name.go names: {x as n} bindings, the scopes they live in, and the reads they answer name.go names: {x as n} bindings, the scopes they live in, the reads they answer, and what each resolves to at load: its target, its reads and the keys they address
namefence.go the load fences over names namefence.go the load fences over names
pick.go a render's named picks: the frames of the name scopes rendering, the memo a pick keeps, and the reads kept under it pick.go a render's named picks: the frames of the name scopes rendering, the memo a pick keeps, and the reads kept under it
env.go renderEnv: what a render reads names and rows through, and the frames a read opens and finds env.go renderEnv: what a render reads names and rows through, the stack of frames it renders in, and the frames a read opens and finds
path.go the dotted-path walks, and proving a path reaches a node path.go the dotted-path walks, and proving a path reaches a node
render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make render.go Fake, the recursive renderer (choices, format strings, expansions) and the reads its tokens make
record.go records: Record, the JSON/CSV/SQL serializers, and their entry points record.go records: Record, the JSON/CSV/SQL serializers, and their entry points
@@ -978,6 +979,7 @@ arm.go the compiled read of one {a|b} alternative or operand: its head,
reference.go one reference's resolution: its sigil, its category, its tail reference.go one reference's resolution: its sigil, its category, its tail
resolve.go resolving templates against the assembled tree in steps, each over every template before the next resolve.go resolving templates against the assembled tree in steps, each over every template before the next
graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences graph.go the node graph: tree walks, containment and render edges, and the cycle and repeat fences
sorted.go sortedNames: a map's keys, sorted
calc.go the {calc()} operand checks: never a number, and a constant zero divisor calc.go the {calc()} operand checks: never a number, and a constant zero divisor
datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype datatype.go column datatypes: DataType, where datatype and null may sit, a column's datatype
valueproof.go the value proof: what a typed column, calc operand or struct field holds, checked at load valueproof.go the value proof: what a typed column, calc operand or struct field holds, checked at load
+15
View File
@@ -11,6 +11,21 @@ type readValue struct {
null bool null bool
} }
// frameStack is the frames of the name scopes rendering, innermost last. A render's env points
// at it, and a repeat iteration's shares it: holding the frames in a renderEnv would move every
// render's env to the heap.
type frameStack struct {
frames []*pickFrame
}
func (st *frameStack) push(f *pickFrame) int {
mark := len(st.frames)
st.frames = append(st.frames, f)
return mark
}
func (st *frameStack) pop(mark int) { st.frames = st.frames[:mark] }
// renderEnv is where a render reads: the frames of the name scopes rendering; and row, the row // renderEnv is where a render reads: the frames of the name scopes rendering; and row, the row
// of the table rendering, which its columns read. It passes by value, and what is read from it // of the table rendering, which its columns read. It passes by value, and what is read from it
// reaches a map key, an interface or a func value only as a copy; else env, and with it every // reaches a map key, an interface or a func value only as a copy; else env, and with it every
+60 -1
View File
@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/larvit/fejkdata/internal/builtinfunc"
"github.com/larvit/fejkdata/internal/grammar" "github.com/larvit/fejkdata/internal/grammar"
) )
@@ -30,7 +31,7 @@ type nameBinding struct {
binder *template binder *template
// Filled by `resolveTemplates`, once every check it runs passed: // Filled by `resolveTemplates`, once every check it runs passed:
nameTarget target nameTarget
// addressed is every key a read of the name lands on or passes, the spelling of the // addressed is every key a read of the name lands on or passes, the spelling of the
// first read reaching it beside it; a pick keeps the draws at these keys, and only these. // first read reaching it beside it; a pick keeps the draws at these keys, and only these.
addressed map[pickKey]string addressed map[pickKey]string
@@ -45,6 +46,13 @@ type nameUse struct {
operand, noRef, nested bool operand, noRef, nested bool
} }
// nameTarget is what a binding resolves to in the assembled tree: start, the node its head names, and
// the path it reads into that node.
type nameTarget struct {
start node
tail []string
}
// bindsField reports whether b binds a path into a field, so a read of it stays in the category. // bindsField reports whether b binds a path into a field, so a read of it stays in the category.
func (b *nameBinding) bindsField() bool { return !grammar.IsRef(b.ref) } func (b *nameBinding) bindsField() bool { return !grammar.IsRef(b.ref) }
@@ -257,3 +265,54 @@ func namedReads(t *template) []namedReadAt {
} }
return out return out
} }
// nameTargets resolves what each binding of ts binds, from its binder's references.
func nameTargets(ts []templateSite) map[*nameBinding]nameTarget {
targets := map[*nameBinding]nameTarget{}
for _, s := range ts {
for _, tok := range s.t.tokens {
if tok.Kind == grammar.NameBind {
a := splitArm(tok.BoundRef, s.t.refs.byName)
targets[s.t.nameScope.bindings[tok.Bound]] = nameTarget{start: s.t.startOf(a.head), tail: a.tail}
}
}
}
return targets
}
// addressedKeys is every key the reads of each name in ts land on or pass, from the name's own
// level, with the spelling of the first read reaching it.
func addressedKeys(ts []templateSite, targets map[*nameBinding]nameTarget) map[*nameBinding]map[pickKey]string {
keys := map[*nameBinding]map[pickKey]string{}
for _, s := range ts {
for _, r := range namedReads(s.t) {
b := r.a.named
if keys[b] == nil {
keys[b] = map[pickKey]string{}
}
for _, key := range r.a.levels[len(targets[b].tail):] {
if _, seen := keys[b][key]; !seen {
keys[b][key] = r.a.spelling
}
}
}
}
return keys
}
func nameUses(ts []templateSite) map[*nameBinding][]nameUse {
uses := map[*nameBinding][]nameUse{}
for _, s := range ts {
for _, r := range namedReads(s.t) {
b := r.a.named
uses[b] = append(uses[b], nameUse{
tail: grammar.JoinSegments(r.a.tail),
in: s.t,
operand: r.operand,
noRef: r.operand && builtinfunc.NoRefOperands(r.o.Fn),
nested: s.t.nameScope != b.scope,
})
}
}
return uses
}
+1 -16
View File
@@ -69,21 +69,6 @@ func newPickFrame(scope *nameScope) *pickFrame {
return f return f
} }
// frameStack is the frames of the name scopes rendering, innermost last. A render's env points
// at it, and a repeat iteration's shares it: holding the frames in a renderEnv would move every
// render's env to the heap.
type frameStack struct {
frames []*pickFrame
}
func (st *frameStack) push(f *pickFrame) int {
mark := len(st.frames)
st.frames = append(st.frames, f)
return mark
}
func (st *frameStack) pop(mark int) { st.frames = st.frames[:mark] }
// scopeAround is the innermost name scope a render of n reads names in, short of the frames n // scopeAround is the innermost name scope a render of n reads names in, short of the frames n
// renders itself, one per iteration of a repeat. // renders itself, one per iteration of a repeat.
func scopeAround(n node) *nameScope { func scopeAround(n node) *nameScope {
@@ -129,7 +114,7 @@ func readName(s *drawstate.State, env renderEnv, a arm) readValue {
if r, done := p.memo.value[a.key()]; done { if r, done := p.memo.value[a.key()]; done {
return r return r
} }
leaf, pins := p.draw(s, a.named.start, a.steps, a.levels) leaf, pins := p.draw(s, a.named.target.start, a.steps, a.levels)
if a.named.bindsField() { if a.named.bindsField() {
return p.renderAt(s, leaf, pins, a.key(), env) return p.renderAt(s, leaf, pins, a.key(), env)
} }
+2 -2
View File
@@ -266,8 +266,8 @@ func recordColumns(t *template) []recordColumn {
if tok.Kind != grammar.NameBind { if tok.Kind != grammar.NameBind {
continue continue
} }
if b := t.nameScope.bindings[tok.Bound]; b.bindsField() && len(b.tail) == 0 && whole[b.start] == nil { if b := t.nameScope.bindings[tok.Bound]; b.bindsField() && len(b.target.tail) == 0 && whole[b.target.start] == nil {
whole[b.start] = b whole[b.target.start] = b
} }
} }
names := sortedNames(t.fields) names := sortedNames(t.fields)
+1 -60
View File
@@ -3,7 +3,6 @@ package fejkdata
import ( import (
"fmt" "fmt"
"github.com/larvit/fejkdata/internal/builtinfunc"
"github.com/larvit/fejkdata/internal/grammar" "github.com/larvit/fejkdata/internal/grammar"
) )
@@ -45,13 +44,6 @@ func resolveInlineTemplates(nodes nodeSet, root map[string]node) error {
return resolveTemplates(ts, root) return resolveTemplates(ts, root)
} }
// nameTarget is what a binding resolves to in the assembled tree: start, the node its head names, and
// the path it reads into that node.
type nameTarget struct {
start node
tail []string
}
// resolvedNames is what resolveTemplates resolved about the names of a set of templates: each binding's // resolvedNames is what resolveTemplates resolved about the names of a set of templates: each binding's
// target, the keys its reads address, and its reads. // target, the keys its reads address, and its reads.
type resolvedNames struct { type resolvedNames struct {
@@ -94,62 +86,11 @@ func resolveTemplates(ts []templateSite, root map[string]node) error {
} }
} }
for b, target := range targets { for b, target := range targets {
b.nameTarget, b.addressed = target, names.addressed[b] b.target, b.addressed = target, names.addressed[b]
} }
return nil return nil
} }
// nameTargets resolves what each binding of ts binds, from its binder's references.
func nameTargets(ts []templateSite) map[*nameBinding]nameTarget {
targets := map[*nameBinding]nameTarget{}
for _, s := range ts {
for _, tok := range s.t.tokens {
if tok.Kind == grammar.NameBind {
a := splitArm(tok.BoundRef, s.t.refs.byName)
targets[s.t.nameScope.bindings[tok.Bound]] = nameTarget{start: s.t.startOf(a.head), tail: a.tail}
}
}
}
return targets
}
// addressedKeys is every key the reads of each name in ts land on or pass, from the name's own
// level, with the spelling of the first read reaching it.
func addressedKeys(ts []templateSite, targets map[*nameBinding]nameTarget) map[*nameBinding]map[pickKey]string {
keys := map[*nameBinding]map[pickKey]string{}
for _, s := range ts {
for _, r := range namedReads(s.t) {
b := r.a.named
if keys[b] == nil {
keys[b] = map[pickKey]string{}
}
for _, key := range r.a.levels[len(targets[b].tail):] {
if _, seen := keys[b][key]; !seen {
keys[b][key] = r.a.spelling
}
}
}
}
return keys
}
func nameUses(ts []templateSite) map[*nameBinding][]nameUse {
uses := map[*nameBinding][]nameUse{}
for _, s := range ts {
for _, r := range namedReads(s.t) {
b := r.a.named
uses[b] = append(uses[b], nameUse{
tail: grammar.JoinSegments(r.a.tail),
in: s.t,
operand: r.operand,
noRef: r.operand && builtinfunc.NoRefOperands(r.o.Fn),
nested: s.t.nameScope != b.scope,
})
}
}
return uses
}
// columnRead is a record's column read by a format that only reads one reference or name, // columnRead is a record's column read by a format that only reads one reference or name,
// which is the column: it takes the column's datatype and null. category and field name it; // which is the column: it takes the column's datatype and null. category and field name it;
// category is "" for a column of the reading template's own record, which checkColumns reaches // category is "" for a column of the reading template's own record, which checkColumns reaches