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
17 changed files with 88 additions and 86 deletions
Showing only changes of commit a7d77bd3ad - Show all commits
+1 -1
View File
@@ -896,7 +896,7 @@ checksum.go the check characters a derivation appends, and the IBAN they sit
transform.go the builtins that rewrite an operand's value, and the ASCII folding transform.go the builtins that rewrite an operand's value, and the ASCII folding
calc.go the {calc()} arithmetic evaluator: parser, eval, validation calc.go the {calc()} arithmetic evaluator: parser, eval, validation
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
value.go the value proof: what a typed column or calc operand holds, checked at load valueproof.go the value proof: what a typed column or calc operand holds, checked at load
data.go the load path: fs.FS folders/files -> merged namespace tree, and the bind pipeline every entry point runs data.go the load path: fs.FS folders/files -> merged namespace tree, and the bind pipeline every entry point runs
cmd/fejkdata/ the fejkdata CLI cmd/fejkdata/ the fejkdata CLI
data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc
+1 -1
View File
@@ -77,7 +77,7 @@ func checkColumns(s nodeScope) error {
items, _ := columnItems(column) items, _ := columnItems(column)
for _, it := range items { for _, it := range items {
if r := it.link.readsColumn; r != nil { if r := it.link.readsColumn; r != nil {
if err := check(r.a.key[1:], r.a.tail[0], r.column); err != nil { if err := check(r.a.head[1:], r.a.tail[0], r.column); err != nil {
return err return err
} }
} }
+2 -2
View File
@@ -90,7 +90,7 @@ func checkOwnFamily(t *template) error {
seen[n] = true seen[n] = true
for _, e := range renderEdges(n) { for _, e := range renderEdges(n) {
if e.readsRef() { if e.readsRef() {
if head, isTable := n.(*template).head(e.read.key).(*table); isTable && head.familyRoot() == own.familyRoot() { if head, isTable := n.(*template).head(e.read.head).(*table); isTable && head.familyRoot() == own.familyRoot() {
return e, head, true return e, head, true
} }
} }
@@ -183,7 +183,7 @@ func readsTable(n node, a arm) bool {
if !isTemplate { if !isTemplate {
return false return false
} }
_, isTable := t.head(a.key).(*table) _, isTable := t.head(a.head).(*table)
return isTable return isTable
} }
+33 -33
View File
@@ -246,25 +246,25 @@ func checkArm(name string, fields map[string]node, wholeToken bool) error {
if err := checkSegments(a); err != nil { if err := checkSegments(a); err != nil {
return err return err
} }
field, ok := fields[a.key] field, ok := fields[a.head]
if !ok { if !ok {
if a.key == "" { if a.head == "" {
return fmt.Errorf("a name is never empty, so this token can name no field") return fmt.Errorf("a name is never empty, so this token can name no field")
} }
if isOption(a.key) { if isOption(a.head) {
return fmt.Errorf("%q is an option and can never be a field", a.key) return fmt.Errorf("%q is an option and can never be a field", a.head)
} }
if len(fields) == 0 { if len(fields) == 0 {
hint := "" hint := ""
if wholeToken && hintableRef(name) { if wholeToken && hintableRef(name) {
hint = fmt.Sprintf(" — write {/%s} to reference the data", name) hint = fmt.Sprintf(" — write {/%s} to reference the data", name)
} }
return fmt.Errorf("no field %q; a token names a sibling field, and this template has none%s", a.key, hint) return fmt.Errorf("no field %q; a token names a sibling field, and this template has none%s", a.head, hint)
} }
return fmt.Errorf("no field %q", a.key) return fmt.Errorf("no field %q", a.head)
} }
if err := checkPath(field, a.tail, a.key); err != nil { if err := checkPath(field, a.tail, a.head); err != nil {
return fmt.Errorf("field %q: %w", a.key, err) return fmt.Errorf("field %q: %w", a.head, err)
} }
return nil return nil
} }
@@ -302,45 +302,45 @@ func checkNoRepeatedArm(body string, names []string) error {
return nil return nil
} }
// arm is one alternative of a {a|b} token or one operand, split into the key // arm is one alternative of a {a|b} token or one operand, split into the head
// `template.head` resolves and the tail of a dotted path into it. A non-empty tail // `template.head` resolves and the tail of a dotted path into it. A non-empty tail
// is what makes the arm a held draw: its head is drawn once per expansion (see // is what makes the arm a held draw: its head is drawn once per expansion (see
// compileOps). // compileOps).
type arm struct { type arm struct {
spelling string // as written, for messages spelling string // as written, for messages
key string head string
tail []string tail []string
levels []string // the key at each level the tail passes through, the arm's key first levels []string // the path at each level the tail passes through, the head first
path string // key and tail, the one path every way of writing this read shares path string // head and tail, the one path every way of writing this read shares
} }
// splitArm splits one name into key and tail. refs maps a reference to what // splitArm splits one name into head and tail. refs maps a reference to what
// linkRefs resolved it to; before linking, a reference is whole. // linkRefs resolved it to; before linking, a reference is whole.
func splitArm(name string, refs map[string]refBinding) arm { func splitArm(name string, refs map[string]refBinding) arm {
if isRef(name) { if isRef(name) {
b, linked := refs[name] b, linked := refs[name]
if !linked || len(b.tail) == 0 { if !linked || len(b.tail) == 0 {
key := name head := name
if linked { if linked {
key = b.key head = b.key
} }
return arm{spelling: name, key: key, path: key} return arm{spelling: name, head: head, path: head}
} }
return pathArm(name, b.key, b.tail) return pathArm(name, b.key, b.tail)
} }
segs, err := splitPath(name) segs, err := splitPath(name)
if err != nil || len(segs) == 1 { if err != nil || len(segs) == 1 {
return arm{spelling: name, key: name, path: name} return arm{spelling: name, head: name, path: name}
} }
return pathArm(name, segs[0], segs[1:]) return pathArm(name, segs[0], segs[1:])
} }
func pathArm(name, key string, segs []string) arm { func pathArm(name, head string, segs []string) arm {
levels := []string{key} levels := []string{head}
for i := 0; i < len(segs)-1; i++ { for i := 0; i < len(segs)-1; i++ {
levels = append(levels, key+"."+strings.Join(segs[:i+1], ".")) levels = append(levels, head+"."+strings.Join(segs[:i+1], "."))
} }
return arm{spelling: name, key: key, tail: segs, levels: levels, path: key + "." + strings.Join(segs, ".")} return arm{spelling: name, head: head, tail: segs, levels: levels, path: head + "." + strings.Join(segs, ".")}
} }
// checkSegments rejects an unfinished path: "{a.}" and "{a..b}" each have a // checkSegments rejects an unfinished path: "{a.}" and "{a..b}" each have a
@@ -350,7 +350,7 @@ func checkSegments(a arm) error {
if len(a.tail) == 0 { if len(a.tail) == 0 {
return nil return nil
} }
if a.key == "" { if a.head == "" {
return fmt.Errorf("path has an empty segment") return fmt.Errorf("path has an empty segment")
} }
for _, seg := range a.tail { for _, seg := range a.tail {
@@ -379,13 +379,13 @@ type op struct {
} }
// formatOps is a compiled format: its ops, the size of its literal text, and the // formatOps is a compiled format: its ops, the size of its literal text, and the
// names it holds. pathKeys maps each key a path starts from to the first such path; // names it holds. pathHeads maps each head a path starts from to the first such path;
// held is every such key plus the fields an operand reads; holder maps each held // held is every such head plus the fields an operand reads; holder maps each held
// name to the first reader holding it, for error messages. // name to the first reader holding it, for error messages.
type formatOps struct { type formatOps struct {
ops []op ops []op
grow int grow int
pathKeys map[string]string pathHeads map[string]string
held map[string]bool held map[string]bool
holder map[string]string holder map[string]string
heldLocal bool // some held name is not a reference path, so expand makes a hold heldLocal bool // some held name is not a reference path, so expand makes a hold
@@ -396,19 +396,19 @@ func (c *formatOps) holdName(a arm, label string) {
c.held = map[string]bool{} c.held = map[string]bool{}
c.holder = map[string]string{} c.holder = map[string]string{}
} }
c.held[a.key] = true c.held[a.head] = true
if !isRef(a.key) || len(a.tail) == 0 { if !isRef(a.head) || len(a.tail) == 0 {
c.heldLocal = true c.heldLocal = true
} }
if _, named := c.holder[a.key]; !named { if _, named := c.holder[a.head]; !named {
c.holder[a.key] = label c.holder[a.head] = label
} }
if len(a.tail) > 0 { if len(a.tail) > 0 {
if c.pathKeys == nil { if c.pathHeads == nil {
c.pathKeys = map[string]string{} c.pathHeads = map[string]string{}
} }
if _, named := c.pathKeys[a.key]; !named { if _, named := c.pathHeads[a.head]; !named {
c.pathKeys[a.key] = a.spelling c.pathHeads[a.head] = a.spelling
} }
} }
} }
+2 -2
View File
@@ -109,7 +109,7 @@ type renderEdge struct {
operand string operand string
} }
func (e renderEdge) readsRef() bool { return isRef(e.read.key) } func (e renderEdge) readsRef() bool { return isRef(e.read.head) }
// reached names an edge as the author spelled it. // reached names an edge as the author spelled it.
func (e renderEdge) reached() string { func (e renderEdge) reached() string {
@@ -133,7 +133,7 @@ func renderEdges(n node) []renderEdge {
case *template: case *template:
var es []renderEdge var es []renderEdge
add := func(a arm, operand string) { add := func(a arm, operand string) {
c := n.head(a.key) c := n.head(a.head)
if c == nil { if c == nil {
return return
} }
+9 -4
View File
@@ -41,7 +41,12 @@ type holdSet struct {
type renderScope struct { type renderScope struct {
set *holdSet set *holdSet
group string group string
row tablePin row renderedRow
}
type renderedRow struct {
t *table
index int
} }
// in is the scope t renders in: its draw group where it names one, else its caller's. // in is the scope t renders in: its draw group where it names one, else its caller's.
@@ -57,9 +62,9 @@ func (sc renderScope) in(t *template) renderScope {
func (sc renderScope) at(n node, pins *pinSet) renderScope { func (sc renderScope) at(n node, pins *pinSet) renderScope {
switch n := n.(type) { switch n := n.(type) {
case *tableRow: case *tableRow:
sc.row = tablePin{n.t, pins.mustRow(n.t)} sc.row = renderedRow{n.t, pins.mustRow(n.t)}
case *tableColumn: case *tableColumn:
sc.row = tablePin{n.t, pins.mustRow(n.t)} sc.row = renderedRow{n.t, pins.mustRow(n.t)}
} }
return sc return sc
} }
@@ -69,7 +74,7 @@ func (sc renderScope) rowOf(t *table) int {
if sc.row.t != t { if sc.row.t != t {
panic(internalError("a column of %s renders in a scope holding no row of it", t.segment)) panic(internalError("a column of %s renders in a scope holding no row of it", t.segment))
} }
return sc.row.row return sc.row.index
} }
func (sc renderScope) groupHold() *groupHold { func (sc renderScope) groupHold() *groupHold {
+1 -1
View File
@@ -761,7 +761,7 @@ func TestRepeatedBareTokenOfAHeldNameIsRejected(t *testing.T) {
} }
func TestReadFieldPanicsOnAPathItCannotHold(t *testing.T) { func TestReadFieldPanicsOnAPathItCannotHold(t *testing.T) {
a := arm{spelling: "w.x", key: "w", tail: []string{"x"}, path: "w.x"} a := arm{spelling: "w.x", head: "w", tail: []string{"x"}, path: "w.x"}
for name, format := range map[string]string{ for name, format := range map[string]string{
"unheld arm with a path": `{"format":"{w}","w":{"format":"{x}","x":"1"}}`, "unheld arm with a path": `{"format":"{w}","w":{"format":"{x}","x":"1"}}`,
"held arm with no hold": `{"format":"{w.x}","w":{"format":"{x}","x":"1"}}`, "held arm with no hold": `{"format":"{w.x}","w":{"format":"{x}","x":"1"}}`,
+19 -19
View File
@@ -15,9 +15,9 @@ 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
} }
readers := pathKeyReaders(t.compiled.ops, t.compiled.pathKeys) readers := pathHeadReaders(t.compiled.ops, t.compiled.pathHeads)
for _, name := range heldNames(t) { for _, name := range heldNames(t) {
if _, isPath := t.compiled.pathKeys[name]; isPath && isRef(name) { if _, isPath := t.compiled.pathHeads[name]; isPath && isRef(name) {
continue // held for the render: drawFence compares every read of it, by path, across the render and its groups continue // held for the render: drawFence compares every read of it, by path, across the render and its groups
} }
if err := checkNameHeld(t, name, readers); err != nil { if err := checkNameHeld(t, name, readers); err != nil {
@@ -36,8 +36,8 @@ func heldNames(t *template) []string {
names = append(names, name) names = append(names, name)
} }
sort.Slice(names, func(i, j int) bool { sort.Slice(names, func(i, j int) bool {
_, pi := t.compiled.pathKeys[names[i]] _, pi := t.compiled.pathHeads[names[i]]
_, pj := t.compiled.pathKeys[names[j]] _, pj := t.compiled.pathHeads[names[j]]
if pi != pj { if pi != pj {
return !pi return !pi
} }
@@ -51,12 +51,12 @@ func heldNames(t *template) []string {
// produces. // produces.
func heldNodes(t *template, name string, readers []reader) map[node]bool { func heldNodes(t *template, name string, readers []reader) map[node]bool {
held := map[node]bool{} held := map[node]bool{}
if _, isPath := t.compiled.pathKeys[name]; !isPath { if _, isPath := t.compiled.pathHeads[name]; !isPath {
operandDraw(t.head(name), held) operandDraw(t.head(name), held)
return held return held
} }
for _, r := range readers { for _, r := range readers {
if r.a.key == name { if r.a.head == name {
coverPath(t.head(name), r.a.tail, held) coverPath(t.head(name), r.a.tail, held)
} }
} }
@@ -71,10 +71,10 @@ func checkNameHeld(t *template, name string, readers []reader) error {
if len(held) == 0 { if len(held) == 0 {
return nil // a fixed head holds nothing to reach return nil // a fixed head holds nothing to reach
} }
reader, isPath := t.compiled.pathKeys[name] reader, isPath := t.compiled.pathHeads[name]
seen := map[node]bool{} seen := map[node]bool{}
for _, e := range renderEdges(t) { for _, e := range renderEdges(t) {
if e.read.key == name || !renders(e.to, held, seen) { if e.read.head == name || !renders(e.to, held, seen) {
continue continue
} }
if isPath { if isPath {
@@ -89,7 +89,7 @@ func checkNameHeld(t *template, name string, readers []reader) error {
func operandReader(t *template, name string) string { func operandReader(t *template, name string) string {
for _, o := range t.compiled.ops { for _, o := range t.compiled.ops {
for _, a := range o.operands { for _, a := range o.operands {
if a.key == name { if a.head == name {
return o.fn return o.fn
} }
} }
@@ -175,8 +175,8 @@ func renders(n node, want, seen map[node]bool) bool {
// disagree. Reads are compared in sorted order, so which pair is reported does not // disagree. Reads are compared in sorted order, so which pair is reported does not
// depend on where the tokens sit. // depend on where the tokens sit.
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held // docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
func checkNoOverlap(ops []op, pathKeys map[string]string) error { func checkNoOverlap(ops []op, pathHeads map[string]string) error {
names := pathKeyReaders(ops, pathKeys) names := pathHeadReaders(ops, pathHeads)
// Stable over one format-order scan, so two readers of one name (a token and a // Stable over one format-order scan, so two readers of one name (a token and a
// calc operand both naming "p") are reported as the format writes them. // calc operand both naming "p") are reported as the format writes them.
sort.SliceStable(names, func(i, j int) bool { return names[i].a.path < names[j].a.path }) sort.SliceStable(names, func(i, j int) bool { return names[i].a.path < names[j].a.path })
@@ -190,25 +190,25 @@ func checkNoOverlap(ops []op, pathKeys map[string]string) error {
return nil return nil
} }
// reader is one way a format reaches a path key, and how to name it. // reader is one way a format reaches a path head, and how to name it.
type reader struct { type reader struct {
a arm a arm
label string label string
} }
// pathKeyReaders lists every way a format reaches a sibling path key, in the order the // pathHeadReaders lists every way a format reaches a sibling path head, in the order the
// format writes them. An operand renders its field, so it names a level exactly // format writes them. An operand renders its field, so it names a level exactly
// as a token does; one scan finds both, which is what puts them in one order. // as a token does; one scan finds both, which is what puts them in one order.
func pathKeyReaders(ops []op, pathKeys map[string]string) []reader { func pathHeadReaders(ops []op, pathHeads map[string]string) []reader {
var names []reader var names []reader
for _, o := range ops { for _, o := range ops {
for _, a := range o.operands { for _, a := range o.operands {
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) { if _, isPathHead := pathHeads[a.head]; isPathHead && !isRef(a.head) {
names = append(names, reader{a, fmt.Sprintf("%s operand %q", o.fn, a.spelling)}) names = append(names, reader{a, fmt.Sprintf("%s operand %q", o.fn, a.spelling)})
} }
} }
for _, a := range o.arms { for _, a := range o.arms {
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) { if _, isPathHead := pathHeads[a.head]; isPathHead && !isRef(a.head) {
names = append(names, reader{a, "token {" + a.spelling + "}"}) names = append(names, reader{a, "token {" + a.spelling + "}"})
} }
} }
@@ -224,11 +224,11 @@ func checkNoRepeatedRead(c formatOps) error {
count := map[string]int{} count := map[string]int{}
for _, o := range c.ops { for _, o := range c.ops {
for _, a := range o.arms { for _, a := range o.arms {
if len(a.tail) > 0 || !c.held[a.key] { if len(a.tail) > 0 || !c.held[a.head] {
continue continue
} }
if count[a.key]++; count[a.key] > 1 { if count[a.head]++; count[a.head] > 1 {
return fmt.Errorf("token {%s} is repeated, and %s holds %q to one draw per expansion; write {%s} once", a.spelling, c.holder[a.key], a.key, a.spelling) return fmt.Errorf("token {%s} is repeated, and %s holds %q to one draw per expansion; write {%s} once", a.spelling, c.holder[a.head], a.head, a.spelling)
} }
} }
} }
+6 -6
View File
@@ -81,12 +81,12 @@ type templateLink struct {
func (*template) isNode() {} func (*template) isNode() {}
// head is the node an arm's key names: a sibling field, or a reference's category. // head is the node an arm's head names: a sibling field, or a reference's category.
func (t *template) head(key string) node { func (t *template) head(name string) node {
if isRef(key) { if isRef(name) {
return t.link.refHeads[key] return t.link.refHeads[name]
} }
return t.fields[key] return t.fields[name]
} }
// compile converts parsed JSON — a category or an inline template — into a node tree, // compile converts parsed JSON — a category or an inline template — into a node tree,
@@ -229,7 +229,7 @@ func (t *template) fixedText() (string, bool) {
// compiled reads. // compiled reads.
func compileFormat(toks []formatToken, refs map[string]refBinding) (formatOps, error) { func compileFormat(toks []formatToken, refs map[string]refBinding) (formatOps, error) {
c := compileOps(toks, refs) c := compileOps(toks, refs)
if err := checkNoOverlap(c.ops, c.pathKeys); err != nil { if err := checkNoOverlap(c.ops, c.pathHeads); err != nil {
return c, err return c, err
} }
return c, checkNoRepeatedRead(c) return c, checkNoRepeatedRead(c)
+1 -1
View File
@@ -143,7 +143,7 @@ func (s *readSurvey) walk(n node, at surveyAt) {
func (s *readSurvey) edge(from node, e renderEdge, at surveyAt) { func (s *readSurvey) edge(from node, e renderEdge, at surveyAt) {
if e.readsRef() { if e.readsRef() {
a := e.read a := e.read
tr := tableReadOf(from.(*template).head(a.key), a, e.to) tr := tableReadOf(from.(*template).head(a.head), a, e.to)
if k := (readKey{at.group, a.path, s.pinsID(&at.pins), s.pinsID(&at.wholePins)}); !s.read[k] { if k := (readKey{at.group, a.path, s.pinsID(&at.pins), s.pinsID(&at.wholePins)}); !s.read[k] {
s.read[k] = true s.read[k] = true
s.reads = append(s.reads, pathRead{at, a, tr}) s.reads = append(s.reads, pathRead{at, a, tr})
+1 -1
View File
@@ -35,7 +35,7 @@ func wantGathered(t *testing.T, tables map[string]*table, label string, gathered
} }
seen := map[traceKey]bool{} seen := map[traceKey]bool{}
trace := func(group, table string, row int, a arm) { trace := func(group, table string, row int, a arm) {
if !isRef(a.key) { if !isRef(a.head) {
return return
} }
r := pathRead{at: surveyAt{group: group}, a: a} r := pathRead{at: surveyAt{group: group}, a: a}
+1 -1
View File
@@ -146,7 +146,7 @@ func columnReadOf(t *template, link templateLink) *columnRead {
return nil return nil
} }
a := splitArm(name, link.refs) a := splitArm(name, link.refs)
target, isTemplate := link.refHeads[a.key].(*template) target, isTemplate := link.refHeads[a.head].(*template)
if !isTemplate || !target.isRecord || len(a.tail) != 1 { if !isTemplate || !target.isRecord || len(a.tail) != 1 {
return nil return nil
} }
+6 -6
View File
@@ -62,7 +62,7 @@ func render(s *session, n node, sc renderScope) string {
case *nullItem: case *nullItem:
return "" return ""
case *table: case *table:
sc.row = tablePin{n, n.drawRow(s)} sc.row = renderedRow{n, n.drawRow(s)}
return expand(s, n.formatTemplate, sc) return expand(s, n.formatTemplate, sc)
case *tableRow: case *tableRow:
return expand(s, n.t.formatTemplate, sc) return expand(s, n.t.formatTemplate, sc)
@@ -159,17 +159,17 @@ func expand(s *session, t *template, sc renderScope) string {
// read one row, either read twice gives one value, and a shown operand is the operand // read one row, either read twice gives one value, and a shown operand is the operand
// computed. Every other name is drawn afresh, so {word} {word} still draws twice. // computed. Every other name is drawn afresh, so {word} {word} still draws twice.
func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue { func readField(s *session, t *template, held *hold, sc renderScope, a arm) readValue {
if isRef(a.key) && len(a.tail) > 0 { if isRef(a.head) && len(a.tail) > 0 {
return readReference(s, t, sc, a) return readReference(s, t, sc, a)
} }
if sc.set.trace != nil { if sc.set.trace != nil {
traceRead(sc.set.trace, t, sc, a) traceRead(sc.set.trace, t, sc, a)
} }
if !t.compiled.held[a.key] { if !t.compiled.held[a.head] {
if len(a.tail) > 0 { if len(a.tail) > 0 {
panic(internalError("%q reads a path into %q, which the expansion does not hold", a.spelling, a.key)) panic(internalError("%q reads a path into %q, which the expansion does not hold", a.spelling, a.head))
} }
return readValue{text: render(s, t.head(a.key), sc)} return readValue{text: render(s, t.head(a.head), sc)}
} }
if held == nil { if held == nil {
panic(internalError("%q reads a name the expansion holds, with no hold to keep it in", a.spelling)) panic(internalError("%q reads a name the expansion holds, with no hold to keep it in", a.spelling))
@@ -191,7 +191,7 @@ func readHeld(s *session, t *template, held *hold, pins *pinSet, sc renderScope,
if r, done := held.value[a.path]; done { if r, done := held.value[a.path]; done {
return r return r
} }
leaf := drawPath(t.head(a.key), a.tail, a.key, &pathDraw{s: s, held: held, pins: pins, a: &a}) leaf := drawPath(t.head(a.head), a.tail, a.head, &pathDraw{s: s, held: held, pins: pins, a: &a})
if pins != nil { if pins != nil {
sc = sc.at(leaf, pins) sc = sc.at(leaf, pins)
} }
+2 -2
View File
@@ -330,8 +330,8 @@ func (t *table) compileRowFormat(format string) error {
} }
for _, name := range tok.names { for _, name := range tok.names {
a := splitArm(name, nil) a := splitArm(name, nil)
if _, ok := t.col[a.key]; !ok && !isRef(a.key) && a.key != "" { if _, ok := t.col[a.head]; !ok && !isRef(a.head) && a.head != "" {
return fmt.Errorf("format names no column %q of %s; the columns are %v", a.key, t.file, t.header) return fmt.Errorf("format names no column %q of %s; the columns are %v", a.head, t.file, t.header)
} }
} }
} }
+2 -2
View File
@@ -1063,7 +1063,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
{"region[12].name", "region=region[12]"}, {"region[12].name", "region=region[12]"},
} { } {
a := splitArm(c.path, nil) a := splitArm(c.path, nil)
head := f.root.children[a.key].(*table) head := f.root.children[a.head].(*table)
tr := tableReadOf(head, a, nil) tr := tableReadOf(head, a, nil)
var sels []string var sels []string
for _, s := range tr.sels { for _, s := range tr.sels {
@@ -1075,7 +1075,7 @@ func TestProbeReportsTheTablesADrawPins(t *testing.T) {
probed, drawn := tr.pins, tr.drawn probed, drawn := tr.pins, tr.drawn
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
var pins pinSet var pins pinSet
drawPath(head, a.tail, a.key, &pathDraw{s: f.rand, pins: &pins}) drawPath(head, a.tail, a.head, &pathDraw{s: f.rand, pins: &pins})
want := map[*table]bool{} want := map[*table]bool{}
pins.each(func(tbl *table, _ int) { pins.each(func(tbl *table, _ int) {
if _, selected := probed.pinned(tbl); !selected { if _, selected := probed.pinned(tbl); !selected {
-3
View File
@@ -28,9 +28,6 @@ ranking `readSurvey.walk` hardest and all seven the unit they would least modify
This round, in order: This round, in order:
- Rename `arm.key` to `head`, as `template.head` reads it, leaving `key` the table's key
column; give `renderScope.row` a type that is not `tablePin`, since it pins nothing;
and name `value.go` by the `valueProof` it holds.
- Delete the prose the seats found empty: "kept whole on purpose" - Delete the prose the seats found empty: "kept whole on purpose"
(`format.go`, `calc.go`), the second copy of the concrete-session rationale (`format.go`, `calc.go`), the second copy of the concrete-session rationale
(`table.drawRow`, `pick`), the doc comments restating `isSelector`, `selectorOf`, (`table.drawRow`, `pick`), the doc comments restating `isSelector`, `selectorOf`,
+1 -1
View File
@@ -164,7 +164,7 @@ func (p *valueProof) proveTemplate(t *template) proven {
case o.kind == nameRead: case o.kind == nameRead:
var leaves []node var leaves []node
for _, a := range o.arms { for _, a := range o.arms {
leaves = append(leaves, pathLeaves(t.head(a.key), a.tail)...) leaves = append(leaves, pathLeaves(t.head(a.head), a.tail)...)
} }
return p.proveUnion(leaves) return p.proveUnion(leaves)
case name == "calc": case name == "calc":