Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -16,8 +16,8 @@ type dataSource struct {
|
|||||||
fsys fs.FS
|
fsys fs.FS
|
||||||
label string
|
label string
|
||||||
onDisk bool
|
onDisk bool
|
||||||
path string
|
diskPath string
|
||||||
dir string
|
baseDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s dataSource) labelled(p string) string {
|
func (s dataSource) labelled(p string) string {
|
||||||
@@ -38,18 +38,18 @@ func loadData(sources []dataSource) (map[string]node, error) {
|
|||||||
root := map[string]node{}
|
root := map[string]node{}
|
||||||
for _, src := range sources {
|
for _, src := range sources {
|
||||||
if src.onDisk {
|
if src.onDisk {
|
||||||
if src.path == "" {
|
if src.diskPath == "" {
|
||||||
return nil, fmt.Errorf("a data path is empty")
|
return nil, fmt.Errorf("a data path is empty")
|
||||||
}
|
}
|
||||||
info, err := os.Stat(src.path)
|
info, err := os.Stat(src.diskPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("data path %s: %w", src.path, err)
|
return nil, fmt.Errorf("data path %s: %w", src.diskPath, err)
|
||||||
}
|
}
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
return nil, fmt.Errorf("%s is not a directory", src.path)
|
return nil, fmt.Errorf("%s is not a directory", src.diskPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir := src.dir
|
dir := src.baseDir
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
dir = "."
|
dir = "."
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ func vocabulary(t *testing.T, files []*ast.File) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// declaredSymbols is every name the package declares at the top level, a method and
|
// declaredSymbols is every name the package declares at the top level, a method and
|
||||||
// a struct field keyed under its type: pinSet.pin, tableRead.whole.
|
// a struct field keyed under its type: pinSet.pin, tableRead.landsWhole.
|
||||||
func declaredSymbols(files []*ast.File) map[string]bool {
|
func declaredSymbols(files []*ast.File) map[string]bool {
|
||||||
names := declaredFuncs(files)
|
names := declaredFuncs(files)
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
|||||||
+2
-2
@@ -72,7 +72,7 @@ func WithSeed(seed uint64) Option {
|
|||||||
// layer several; the last wins a name clash.
|
// layer several; the last wins a name clash.
|
||||||
func WithDataPath(dir string) Option {
|
func WithDataPath(dir string) Option {
|
||||||
return func(c *config) {
|
return func(c *config) {
|
||||||
c.sources = append(c.sources, dataSource{fsys: os.DirFS(dir), label: dir, onDisk: true, path: dir})
|
c.sources = append(c.sources, dataSource{fsys: os.DirFS(dir), label: dir, onDisk: true, diskPath: dir})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ func New(opts ...Option) (*Generator, error) {
|
|||||||
}
|
}
|
||||||
var sources []dataSource
|
var sources []dataSource
|
||||||
if c.shipped {
|
if c.shipped {
|
||||||
sources = append(sources, dataSource{fsys: shippedFS, dir: "data"})
|
sources = append(sources, dataSource{fsys: shippedFS, baseDir: "data"})
|
||||||
}
|
}
|
||||||
sources = append(sources, c.sources...)
|
sources = append(sources, c.sources...)
|
||||||
if len(sources) == 0 {
|
if len(sources) == 0 {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
// {…} token.
|
// {…} token.
|
||||||
type ftoken struct {
|
type ftoken struct {
|
||||||
kind byte // 'l' literal rune, 't' token body
|
kind byte // 'l' literal rune, 't' token body
|
||||||
r rune
|
char rune
|
||||||
body string
|
body string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ func eachToken(format string, fn func(ftoken) error) error {
|
|||||||
switch c := rs[i]; c {
|
switch c := rs[i]; c {
|
||||||
case '{':
|
case '{':
|
||||||
if i+1 < len(rs) && rs[i+1] == '{' {
|
if i+1 < len(rs) && rs[i+1] == '{' {
|
||||||
t.kind, t.r = 'l', '{'
|
t.kind, t.char = 'l', '{'
|
||||||
i++
|
i++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -42,13 +42,13 @@ func eachToken(format string, fn func(ftoken) error) error {
|
|||||||
i = end
|
i = end
|
||||||
case '}':
|
case '}':
|
||||||
if i+1 < len(rs) && rs[i+1] == '}' {
|
if i+1 < len(rs) && rs[i+1] == '}' {
|
||||||
t.kind, t.r = 'l', '}'
|
t.kind, t.char = 'l', '}'
|
||||||
i++
|
i++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return fmt.Errorf("lone '}' in %q; a literal brace is written }}", format)
|
return fmt.Errorf("lone '}' in %q; a literal brace is written }}", format)
|
||||||
default:
|
default:
|
||||||
t.kind, t.r = 'l', c
|
t.kind, t.char = 'l', c
|
||||||
}
|
}
|
||||||
if err := fn(t); err != nil {
|
if err := fn(t); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -81,7 +81,7 @@ func parseFormat(format string) ([]formatToken, error) {
|
|||||||
}
|
}
|
||||||
err := eachToken(format, func(t ftoken) error {
|
err := eachToken(format, func(t ftoken) error {
|
||||||
if t.kind == 'l' {
|
if t.kind == 'l' {
|
||||||
lit.WriteRune(t.r)
|
lit.WriteRune(t.char)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
flush()
|
flush()
|
||||||
@@ -301,7 +301,7 @@ type arm struct {
|
|||||||
key string
|
key string
|
||||||
tail []string
|
tail []string
|
||||||
levels []string // the key at each level the tail passes through, the head's first
|
levels []string // the key at each level the tail passes through, the head's first
|
||||||
path string // key and tail, the one spelling every way of writing this read shares
|
path string // key 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 key and tail. refs maps a reference to what
|
||||||
|
|||||||
+2
-2
@@ -188,8 +188,8 @@ func checkNoOverlap(ops []op, bound map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reader is one way a format reaches a bound field: as written, by its one
|
// reader is one way a format reaches a bound field: as written, by its path,
|
||||||
// spelling, and how to name it.
|
// and how to name it.
|
||||||
type reader struct{ spelling, path, label string }
|
type reader struct{ spelling, path, label string }
|
||||||
|
|
||||||
// boundReaders lists every way a format reaches a bound sibling field, in the order the
|
// boundReaders lists every way a format reaches a bound sibling field, in the order the
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ func tableFacts(t *table) string {
|
|||||||
for _, o := range []struct {
|
for _, o := range []struct {
|
||||||
name string
|
name string
|
||||||
col int
|
col int
|
||||||
}{{"key", t.keyCol}, {"name", t.nameCol}, {"weight", t.weightCol}, {"parent", t.parentCol}} {
|
}{{"key", t.keyIndex}, {"name", t.nameIndex}, {"weight", t.weightIndex}, {"parent", t.parentIndex}} {
|
||||||
if o.col >= 0 {
|
if o.col >= 0 {
|
||||||
b.WriteString("\t" + o.name + " " + t.header[o.col])
|
b.WriteString("\t" + o.name + " " + t.header[o.col])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (f *Generator) structShapeOf(t reflect.Type) (*structShape, error) {
|
|||||||
if label == "" {
|
if label == "" {
|
||||||
label = "struct"
|
label = "struct"
|
||||||
}
|
}
|
||||||
sc := &structCompile{categories: f.categories, visiting: map[reflect.Type]bool{}, structs: maxStructs}
|
sc := &structCompile{categories: f.categories, visiting: map[reflect.Type]bool{}, structsLeft: maxStructs}
|
||||||
shape, err := sc.compileShape(t, label)
|
shape, err := sc.compileShape(t, label)
|
||||||
if err == nil && shape.empty() {
|
if err == nil && shape.empty() {
|
||||||
err = fmt.Errorf("%s has no fake tags, so nothing to fill", t)
|
err = fmt.Errorf("%s has no fake tags, so nothing to fill", t)
|
||||||
@@ -84,7 +84,7 @@ func (s *structShape) empty() bool { return s.record == nil && len(s.nested) ==
|
|||||||
type structCompile struct {
|
type structCompile struct {
|
||||||
categories map[string]node
|
categories map[string]node
|
||||||
visiting map[reflect.Type]bool
|
visiting map[reflect.Type]bool
|
||||||
structs int
|
structsLeft int
|
||||||
}
|
}
|
||||||
|
|
||||||
// structFields gathers what one struct type fills: its tagged fields, those its embedded
|
// structFields gathers what one struct type fills: its tagged fields, those its embedded
|
||||||
@@ -117,7 +117,7 @@ func (sc *structCompile) compileShape(t reflect.Type, label string) (*structShap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sc *structCompile) spend(label string) error {
|
func (sc *structCompile) spend(label string) error {
|
||||||
if sc.structs--; sc.structs >= 0 {
|
if sc.structsLeft--; sc.structsLeft >= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf(`%s: the struct fields reach more than %d structs; leave a struct field unfilled with fake:"-"`, label, maxStructs)
|
return fmt.Errorf(`%s: the struct fields reach more than %d structs; leave a struct field unfilled with fake:"-"`, label, maxStructs)
|
||||||
@@ -252,13 +252,13 @@ func checkTaggedType(sf reflect.StructField) error {
|
|||||||
|
|
||||||
// compileRecord compiles the tagged fields of t as one record, and proves each column holds
|
// compileRecord compiles the tagged fields of t as one record, and proves each column holds
|
||||||
// only what its field's Go type can.
|
// only what its field's Go type can.
|
||||||
func (s *structShape) compileRecord(root map[string]node, t reflect.Type, label string, tags map[string]any) error {
|
func (s *structShape) compileRecord(categories map[string]node, t reflect.Type, label string, tags map[string]any) error {
|
||||||
tags["format"] = ""
|
tags["format"] = ""
|
||||||
n, err := compile(tags)
|
n, err := compile(tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", label, err)
|
return fmt.Errorf("%s: %w", label, err)
|
||||||
}
|
}
|
||||||
if err := bindInline(n, label, root, checkRenders); err != nil {
|
if err := bindInline(n, label, categories, checkRenders); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
record, columns, err := recordOf(n)
|
record, columns, err := recordOf(n)
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ type table struct {
|
|||||||
wholeRow *tableRow // the pinned row rendered by the format
|
wholeRow *tableRow // the pinned row rendered by the format
|
||||||
cells []string // rows × columns, flat
|
cells []string // rows × columns, flat
|
||||||
cellTemplates map[int]*template
|
cellTemplates map[int]*template
|
||||||
keyCol int // keyCol through parentCol are -1 where the option is absent
|
keyIndex int // keyIndex through parentIndex are column indexes, -1 where the option is absent
|
||||||
nameCol int
|
nameIndex int
|
||||||
weightCol int
|
weightIndex int
|
||||||
parentCol int
|
parentIndex int
|
||||||
cum []float64 // cumulative weights, nil when uniform
|
cum []float64 // cumulative weights, nil when uniform
|
||||||
byKey map[string]int
|
byKey map[string]int
|
||||||
parentT *table
|
parentT *table
|
||||||
@@ -93,7 +93,7 @@ func compileTable(m map[string]any, category string, files *categoryFiles) (*tab
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := &table{category: category, file: o.rows, keyCol: -1, nameCol: -1, weightCol: -1, parentCol: -1}
|
t := &table{category: category, file: o.rows, keyIndex: -1, nameIndex: -1, weightIndex: -1, parentIndex: -1}
|
||||||
if err := t.parseRows(data); err != nil {
|
if err := t.parseRows(data); err != nil {
|
||||||
return nil, fmt.Errorf("%s: %w", o.rows, err)
|
return nil, fmt.Errorf("%s: %w", o.rows, err)
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ func (t *table) bindOptions(o tableOptionValues) error {
|
|||||||
for _, opt := range []struct {
|
for _, opt := range []struct {
|
||||||
name, value string
|
name, value string
|
||||||
into *int
|
into *int
|
||||||
}{{"key", o.keyColumn, &t.keyCol}, {"name", o.nameColumn, &t.nameCol}, {"parent", o.parentColumn, &t.parentCol}, {"weight", o.weightColumn, &t.weightCol}} {
|
}{{"key", o.keyColumn, &t.keyIndex}, {"name", o.nameColumn, &t.nameIndex}, {"parent", o.parentColumn, &t.parentIndex}, {"weight", o.weightColumn, &t.weightIndex}} {
|
||||||
if opt.value == "" {
|
if opt.value == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ func (t *table) bindOptions(o tableOptionValues) error {
|
|||||||
}
|
}
|
||||||
*opt.into = i
|
*opt.into = i
|
||||||
}
|
}
|
||||||
if t.nameCol >= 0 && t.keyCol < 0 && t.parentCol < 0 {
|
if t.nameIndex >= 0 && t.keyIndex < 0 && t.parentIndex < 0 {
|
||||||
return fmt.Errorf("name selects a row as a key does, and lists the rows it matches by their keys, so it needs a key column, or a parent inside which each name is one row; add key")
|
return fmt.Errorf("name selects a row as a key does, and lists the rows it matches by their keys, so it needs a key column, or a parent inside which each name is one row; add key")
|
||||||
}
|
}
|
||||||
if err := t.indexKeys(); err != nil {
|
if err := t.indexKeys(); err != nil {
|
||||||
@@ -237,14 +237,14 @@ func (t *table) bindOptions(o tableOptionValues) error {
|
|||||||
|
|
||||||
// proveNamesInsideParent proves a name without a key names one row inside its parent.
|
// proveNamesInsideParent proves a name without a key names one row inside its parent.
|
||||||
func (t *table) proveNamesInsideParent() error {
|
func (t *table) proveNamesInsideParent() error {
|
||||||
if t.keyCol >= 0 || t.nameCol < 0 {
|
if t.keyIndex >= 0 || t.nameIndex < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
inside := make(map[string]int, t.rowCount())
|
inside := make(map[string]int, t.rowCount())
|
||||||
for r := 0; r < t.rowCount(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parentCol) + "\t" + t.cell(r, t.nameCol)
|
k := t.cell(r, t.parentIndex) + "\t" + t.cell(r, t.nameIndex)
|
||||||
if first, dup := inside[k]; dup {
|
if first, dup := inside[k]; dup {
|
||||||
return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.nameCol), first+2, t.header[t.parentCol], t.cell(r, t.parentCol))
|
return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.nameIndex), first+2, t.header[t.parentIndex], t.cell(r, t.parentIndex))
|
||||||
}
|
}
|
||||||
inside[k] = r
|
inside[k] = r
|
||||||
}
|
}
|
||||||
@@ -253,12 +253,12 @@ func (t *table) proveNamesInsideParent() error {
|
|||||||
|
|
||||||
// indexKeys proves every key names one row, and keeps the index a link is proved by.
|
// indexKeys proves every key names one row, and keeps the index a link is proved by.
|
||||||
func (t *table) indexKeys() error {
|
func (t *table) indexKeys() error {
|
||||||
if t.keyCol < 0 {
|
if t.keyIndex < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.byKey = make(map[string]int, t.rowCount())
|
t.byKey = make(map[string]int, t.rowCount())
|
||||||
for r := 0; r < t.rowCount(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.keyCol)
|
k := t.cell(r, t.keyIndex)
|
||||||
if k == "" {
|
if k == "" {
|
||||||
return fmt.Errorf("%s line %d: the key is empty", t.file, r+2)
|
return fmt.Errorf("%s line %d: the key is empty", t.file, r+2)
|
||||||
}
|
}
|
||||||
@@ -267,8 +267,8 @@ func (t *table) indexKeys() error {
|
|||||||
}
|
}
|
||||||
t.byKey[k] = r
|
t.byKey[k] = r
|
||||||
}
|
}
|
||||||
for r := 0; r < t.rowCount() && t.nameCol >= 0; r++ {
|
for r := 0; r < t.rowCount() && t.nameIndex >= 0; r++ {
|
||||||
if n := t.cell(r, t.nameCol); t.byKey[n] != r {
|
if n := t.cell(r, t.nameIndex); t.byKey[n] != r {
|
||||||
if other, isKey := t.byKey[n]; isKey {
|
if other, isKey := t.byKey[n]; isKey {
|
||||||
return fmt.Errorf("%s line %d: name %q is the key of line %d, which a selector reads first, so the name could never select this row", t.file, r+2, n, other+2)
|
return fmt.Errorf("%s line %d: name %q is the key of line %d, which a selector reads first, so the name could never select this row", t.file, r+2, n, other+2)
|
||||||
}
|
}
|
||||||
@@ -279,15 +279,15 @@ func (t *table) indexKeys() error {
|
|||||||
|
|
||||||
// sumWeights proves every weight is a positive number and builds the cumulative table.
|
// sumWeights proves every weight is a positive number and builds the cumulative table.
|
||||||
func (t *table) sumWeights() error {
|
func (t *table) sumWeights() error {
|
||||||
if t.weightCol < 0 {
|
if t.weightIndex < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.cum = make([]float64, t.rowCount())
|
t.cum = make([]float64, t.rowCount())
|
||||||
total := 0.0
|
total := 0.0
|
||||||
for r := range t.cum {
|
for r := range t.cum {
|
||||||
w, err := strconv.ParseFloat(t.cell(r, t.weightCol), 64)
|
w, err := strconv.ParseFloat(t.cell(r, t.weightIndex), 64)
|
||||||
if err != nil || math.IsInf(w, 0) || math.IsNaN(w) || w <= 0 {
|
if err != nil || math.IsInf(w, 0) || math.IsNaN(w) || w <= 0 {
|
||||||
return fmt.Errorf("%s line %d: weight %q is not a positive number", t.file, r+2, t.cell(r, t.weightCol))
|
return fmt.Errorf("%s line %d: weight %q is not a positive number", t.file, r+2, t.cell(r, t.weightIndex))
|
||||||
}
|
}
|
||||||
total += w
|
total += w
|
||||||
t.cum[r] = total
|
t.cum[r] = total
|
||||||
@@ -302,7 +302,7 @@ func (t *table) sumWeights() error {
|
|||||||
func (t *table) checkCells() error {
|
func (t *table) checkCells() error {
|
||||||
for i, cell := range t.cells {
|
for i, cell := range t.cells {
|
||||||
row, col := i/len(t.header), i%len(t.header)
|
row, col := i/len(t.header), i%len(t.header)
|
||||||
if (col == t.keyCol || col == t.nameCol) && strings.ContainsAny(cell, inSelector) {
|
if (col == t.keyIndex || col == t.nameIndex) && strings.ContainsAny(cell, inSelector) {
|
||||||
return fmt.Errorf("line %d: %s %q contains %q, which a selector cannot spell", row+2, t.header[col], cell, cell[strings.IndexAny(cell, inSelector):][:1])
|
return fmt.Errorf("line %d: %s %q contains %q, which a selector cannot spell", row+2, t.header[col], cell, cell[strings.IndexAny(cell, inSelector):][:1])
|
||||||
}
|
}
|
||||||
if strings.IndexByte(cell, '{') < 0 && strings.IndexByte(cell, '}') < 0 {
|
if strings.IndexByte(cell, '{') < 0 && strings.IndexByte(cell, '}') < 0 {
|
||||||
@@ -379,7 +379,7 @@ func linkTables(root map[string]node) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case *table:
|
case *table:
|
||||||
if n.parentCol < 0 {
|
if n.parentIndex < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := n.linkParent(path, children); err != nil {
|
if err := n.linkParent(path, children); err != nil {
|
||||||
@@ -393,24 +393,24 @@ func linkTables(root map[string]node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) linkParent(path string, siblings map[string]node) error {
|
func (t *table) linkParent(path string, siblings map[string]node) error {
|
||||||
name := t.header[t.parentCol]
|
name := t.header[t.parentIndex]
|
||||||
p, ok := siblings[name].(*table)
|
p, ok := siblings[name].(*table)
|
||||||
switch {
|
switch {
|
||||||
case siblings[name] == nil:
|
case siblings[name] == nil:
|
||||||
return fmt.Errorf("parent %q names no table beside it", name)
|
return fmt.Errorf("parent %q names no table beside it", name)
|
||||||
case !ok:
|
case !ok:
|
||||||
return fmt.Errorf("parent %q is not a table; a link column reads a table's key", name)
|
return fmt.Errorf("parent %q is not a table; a link column reads a table's key", name)
|
||||||
case p.keyCol < 0:
|
case p.keyIndex < 0:
|
||||||
return fmt.Errorf("parent %q has no key column to link to", name)
|
return fmt.Errorf("parent %q has no key column to link to", name)
|
||||||
}
|
}
|
||||||
var ancestors []*table
|
var ancestors []*table
|
||||||
for q, seen := p, map[*table]bool{t: true}; q != nil; q, _ = siblings[q.header[q.parentCol]].(*table) {
|
for q, seen := p, map[*table]bool{t: true}; q != nil; q, _ = siblings[q.header[q.parentIndex]].(*table) {
|
||||||
if seen[q] {
|
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.category)
|
||||||
}
|
}
|
||||||
seen[q] = true
|
seen[q] = true
|
||||||
ancestors = append(ancestors, q)
|
ancestors = append(ancestors, q)
|
||||||
if q.parentCol < 0 {
|
if q.parentIndex < 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,14 +421,14 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
|
|||||||
}
|
}
|
||||||
linked := make(map[string]bool, p.rowCount())
|
linked := make(map[string]bool, p.rowCount())
|
||||||
for r := 0; r < t.rowCount(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parentCol)
|
k := t.cell(r, t.parentIndex)
|
||||||
if _, ok := p.byKey[k]; !ok {
|
if _, ok := p.byKey[k]; !ok {
|
||||||
return fmt.Errorf("%s line %d: %s %q is no key of %s", t.file, r+2, name, k, p.file)
|
return fmt.Errorf("%s line %d: %s %q is no key of %s", t.file, r+2, name, k, p.file)
|
||||||
}
|
}
|
||||||
linked[k] = true
|
linked[k] = true
|
||||||
}
|
}
|
||||||
for r := 0; r < p.rowCount(); r++ {
|
for r := 0; r < p.rowCount(); r++ {
|
||||||
if k := p.cell(r, p.keyCol); !linked[k] {
|
if k := p.cell(r, p.keyIndex); !linked[k] {
|
||||||
return fmt.Errorf("%s links no row to %s %q; every %s row needs one, or drop line %d of %s", t.file, name, k, name, r+2, p.file)
|
return fmt.Errorf("%s links no row to %s %q; every %s row needs one, or drop line %d of %s", t.file, name, k, name, r+2, p.file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,17 +442,17 @@ func (t *table) linkParent(path string, siblings map[string]node) error {
|
|||||||
|
|
||||||
func (t *table) indexed() *tableIndex {
|
func (t *table) indexed() *tableIndex {
|
||||||
t.once.Do(func() {
|
t.once.Do(func() {
|
||||||
if t.nameCol >= 0 {
|
if t.nameIndex >= 0 {
|
||||||
t.index.byName = make(map[string][]int, t.rowCount())
|
t.index.byName = make(map[string][]int, t.rowCount())
|
||||||
for r := 0; r < t.rowCount(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
n := t.cell(r, t.nameCol)
|
n := t.cell(r, t.nameIndex)
|
||||||
t.index.byName[n] = append(t.index.byName[n], r)
|
t.index.byName[n] = append(t.index.byName[n], r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.parentCol >= 0 {
|
if t.parentIndex >= 0 {
|
||||||
t.index.rowsByParent = map[string][]int{}
|
t.index.rowsByParent = map[string][]int{}
|
||||||
for r := 0; r < t.rowCount(); r++ {
|
for r := 0; r < t.rowCount(); r++ {
|
||||||
k := t.cell(r, t.parentCol)
|
k := t.cell(r, t.parentIndex)
|
||||||
t.index.rowsByParent[k] = append(t.index.rowsByParent[k], r)
|
t.index.rowsByParent[k] = append(t.index.rowsByParent[k], r)
|
||||||
}
|
}
|
||||||
if t.cum != nil {
|
if t.cum != nil {
|
||||||
@@ -460,7 +460,7 @@ func (t *table) indexed() *tableIndex {
|
|||||||
for k, rows := range t.index.rowsByParent {
|
for k, rows := range t.index.rowsByParent {
|
||||||
cum, total := make([]float64, len(rows)), 0.0
|
cum, total := make([]float64, len(rows)), 0.0
|
||||||
for i, r := range rows {
|
for i, r := range rows {
|
||||||
w, _ := strconv.ParseFloat(t.cell(r, t.weightCol), 64) // sumWeights proved it
|
w, _ := strconv.ParseFloat(t.cell(r, t.weightIndex), 64) // sumWeights proved it
|
||||||
total += w
|
total += w
|
||||||
cum[i] = total
|
cum[i] = total
|
||||||
}
|
}
|
||||||
@@ -489,7 +489,7 @@ func pickCum(s *session, cum []float64) int {
|
|||||||
// drawUnder picks a row among those linked to parent row pr.
|
// drawUnder picks a row among those linked to parent row pr.
|
||||||
func (t *table) drawUnder(s *session, pr int) int {
|
func (t *table) drawUnder(s *session, pr int) int {
|
||||||
ix := t.indexed()
|
ix := t.indexed()
|
||||||
k := t.parentT.cell(pr, t.parentT.keyCol)
|
k := t.parentT.cell(pr, t.parentT.keyIndex)
|
||||||
rows := ix.rowsByParent[k]
|
rows := ix.rowsByParent[k]
|
||||||
if ix.childCum == nil {
|
if ix.childCum == nil {
|
||||||
return rows[s.IntN(len(rows))]
|
return rows[s.IntN(len(rows))]
|
||||||
@@ -536,7 +536,7 @@ func (t *table) descendant(name string) *table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parentRow is the row of t's parent that row r links to.
|
// parentRow is the row of t's parent that row r links to.
|
||||||
func (t *table) parentRow(r int) int { return t.parentT.byKey[t.cell(r, t.parentCol)] }
|
func (t *table) parentRow(r int) int { return t.parentT.byKey[t.cell(r, t.parentIndex)] }
|
||||||
|
|
||||||
// descends reports whether a is an ancestor of t.
|
// descends reports whether a is an ancestor of t.
|
||||||
func (t *table) descends(a *table) bool {
|
func (t *table) descends(a *table) bool {
|
||||||
@@ -561,7 +561,7 @@ func (t *table) under(r int, a *table, pr int) bool {
|
|||||||
// find is the row a selector names: by key first, then by name, where a name
|
// find is the row a selector names: by key first, then by name, where a name
|
||||||
// naming several rows resolves inside the ancestors pinned.
|
// naming several rows resolves inside the ancestors pinned.
|
||||||
func (t *table) find(sel string, pins *pinSet) (int, error) {
|
func (t *table) find(sel string, pins *pinSet) (int, error) {
|
||||||
if t.keyCol < 0 && t.nameCol < 0 {
|
if t.keyIndex < 0 && t.nameIndex < 0 {
|
||||||
return 0, fmt.Errorf("%s has no key or name column to select a row by", t.path)
|
return 0, fmt.Errorf("%s has no key or name column to select a row by", t.path)
|
||||||
}
|
}
|
||||||
if r, ok := t.byKey[sel]; ok {
|
if r, ok := t.byKey[sel]; ok {
|
||||||
@@ -585,14 +585,14 @@ func (t *table) find(sel string, pins *pinSet) (int, error) {
|
|||||||
func (t *table) ambiguous(sel string, rows []int) error {
|
func (t *table) ambiguous(sel string, rows []int) error {
|
||||||
spellings := make([]string, len(rows))
|
spellings := make([]string, len(rows))
|
||||||
for i, r := range rows {
|
for i, r := range rows {
|
||||||
if t.keyCol < 0 {
|
if t.keyIndex < 0 {
|
||||||
spellings[i] = t.selectorSpelling(r)
|
spellings[i] = t.selectorSpelling(r)
|
||||||
} else {
|
} else {
|
||||||
spellings[i] = t.cell(r, t.keyCol)
|
spellings[i] = t.cell(r, t.keyIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listed := strings.Join(spellings, ", ")
|
listed := strings.Join(spellings, ", ")
|
||||||
if t.keyCol < 0 {
|
if t.keyIndex < 0 {
|
||||||
return fmt.Errorf("%q names %d rows of %s; select it inside its %s, one of %s", sel, len(rows), t.path, t.parentT.path, listed)
|
return fmt.Errorf("%q names %d rows of %s; select it inside its %s, one of %s", sel, len(rows), t.path, t.parentT.path, listed)
|
||||||
}
|
}
|
||||||
inside := ""
|
inside := ""
|
||||||
@@ -606,10 +606,10 @@ func (t *table) ambiguous(sel string, rows []int) error {
|
|||||||
// by name inside its parent's row.
|
// by name inside its parent's row.
|
||||||
func (t *table) selectorSpelling(r int) string {
|
func (t *table) selectorSpelling(r int) string {
|
||||||
switch {
|
switch {
|
||||||
case t.keyCol >= 0:
|
case t.keyIndex >= 0:
|
||||||
return t.path + "[" + t.cell(r, t.keyCol) + "]"
|
return t.path + "[" + t.cell(r, t.keyIndex) + "]"
|
||||||
case t.nameCol >= 0:
|
case t.nameIndex >= 0:
|
||||||
return t.parentT.selectorSpelling(t.parentRow(r)) + "." + t.category + "[" + t.cell(r, t.nameCol) + "]"
|
return t.parentT.selectorSpelling(t.parentRow(r)) + "." + t.category + "[" + t.cell(r, t.nameIndex) + "]"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s line %d", t.file, r+2)
|
return fmt.Sprintf("%s line %d", t.file, r+2)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user