Name pathKeys, pathKeyReaders, proveKeys and readValue's hold for what they hold, and give no token kind to the zero value
This commit is contained in:
@@ -63,7 +63,7 @@ In [docs/decisions.md](docs/decisions.md):
|
||||
- A column of one reference alone is the column it reads
|
||||
- A typed column's calc is refused unless proven
|
||||
- `Column` carries text, not a Go value
|
||||
- `hold` names what a draw is kept in, `draw` the draw itself
|
||||
- `hold` names what a draw is kept in, `readValue` what a read drew
|
||||
- One name, one meaning
|
||||
- The package stays flat
|
||||
- The performance gate asserts allocations, not wall-clock time
|
||||
@@ -77,7 +77,7 @@ In [docs/decisions.md](docs/decisions.md):
|
||||
- A parent row with no child row is a load error
|
||||
- The choice-of-rows fence guards a data file's root, and requires string fields
|
||||
- A table is a record of string columns
|
||||
- The key index is built at load, the rest on first draw
|
||||
- The key map is built at load, the rest on first draw
|
||||
- Two categories may name one TSV
|
||||
- The rows of a table are alternatives
|
||||
- A table never reaches its own family, by any route
|
||||
|
||||
+2
-2
@@ -302,7 +302,7 @@ in.
|
||||
`Value` is the rendered string beside `DataType` and `Null`, which each serializer
|
||||
writes as the load check proved it; a `Value any` would hand every caller a type switch.
|
||||
|
||||
## `hold` names what a draw is kept in, `draw` the draw itself
|
||||
## `hold` names what a draw is kept in, `readValue` what a read drew
|
||||
|
||||
Goal 2 wants a name to reach one unit, so a unit takes the stem of what it is, and a
|
||||
file the stem of the units it holds.
|
||||
@@ -439,7 +439,7 @@ Its columns are the CSV header and the `INSERT` column list, fixed by the TSV he
|
||||
a table is a record by construction; every column is a string until a typed column
|
||||
option earns its place.
|
||||
|
||||
## The key index is built at load, the rest on first draw
|
||||
## The key map is built at load, the rest on first draw
|
||||
|
||||
A link is proved against the parent's keys and a key's uniqueness is a data mistake, so
|
||||
both are load-time; the per-parent child lists serve only a draw, so they wait for the
|
||||
|
||||
@@ -70,7 +70,7 @@ type formatToken struct {
|
||||
type tokenKind uint8
|
||||
|
||||
const (
|
||||
builtinCall tokenKind = iota
|
||||
builtinCall tokenKind = iota + 1
|
||||
fieldAlternation
|
||||
literalRun
|
||||
)
|
||||
@@ -376,18 +376,18 @@ type op struct {
|
||||
}
|
||||
|
||||
// formatOps is a compiled format: its ops, the size of its literal text (to size
|
||||
// the render buffer), and the names drawn once per expansion. pathLevels maps each level
|
||||
// a path reads into to the first such path; held is every such level plus the
|
||||
// the render buffer), and the names drawn once per expansion. pathKeys maps each key
|
||||
// a path starts from to the first such path; held is every such key plus the
|
||||
// fields an operand reads; holder maps each held name to the first reader holding
|
||||
// it, for error messages. The maps are nil when the format holds nothing, so data
|
||||
// that holds nothing carries no render-time cost.
|
||||
type formatOps struct {
|
||||
ops []op
|
||||
grow int
|
||||
pathLevels map[string]string
|
||||
held map[string]bool
|
||||
holder map[string]string
|
||||
heldLocal bool // some held name is kept by the expansion itself rather than the render's hold
|
||||
ops []op
|
||||
grow int
|
||||
pathKeys map[string]string
|
||||
held map[string]bool
|
||||
holder map[string]string
|
||||
heldLocal bool // some held name is kept by the expansion itself rather than the render's hold
|
||||
}
|
||||
|
||||
func (c *formatOps) holdName(a arm, label string) {
|
||||
@@ -403,11 +403,11 @@ func (c *formatOps) holdName(a arm, label string) {
|
||||
c.holder[a.key] = label
|
||||
}
|
||||
if len(a.tail) > 0 {
|
||||
if c.pathLevels == nil {
|
||||
c.pathLevels = map[string]string{}
|
||||
if c.pathKeys == nil {
|
||||
c.pathKeys = map[string]string{}
|
||||
}
|
||||
if _, named := c.pathLevels[a.key]; !named {
|
||||
c.pathLevels[a.key] = a.spelling
|
||||
if _, named := c.pathKeys[a.key]; !named {
|
||||
c.pathKeys[a.key] = a.spelling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ type renderEdge struct {
|
||||
operand string
|
||||
}
|
||||
|
||||
// reached names an edge as the author spelled it, the vocabulary levelReaders uses
|
||||
// reached names an edge as the author spelled it, the vocabulary pathKeyReaders uses
|
||||
// for the sibling fence.
|
||||
func (e renderEdge) reached() string {
|
||||
if e.operand != "" {
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
// hold is what has already been drawn for held names: the variant each was drawn as, so every
|
||||
// path under it reads one row; the draw each read made, by its path, so the same read
|
||||
// written twice reads one draw; and the table rows those reads pinned. An expansion keeps one for
|
||||
// path under it reads one row; the value each read produced, by its path, so the same read
|
||||
// written twice reads one value; and the table rows those reads pinned. An expansion keeps one for
|
||||
// its sibling names, and a render one per group for its reference paths.
|
||||
type hold struct {
|
||||
variant map[string]node
|
||||
|
||||
+13
-13
@@ -15,9 +15,9 @@ func heldCheck(path string, n node) error {
|
||||
if !ok || len(t.held) == 0 {
|
||||
return nil
|
||||
}
|
||||
readers := levelReaders(t.ops, t.pathLevels)
|
||||
readers := pathKeyReaders(t.ops, t.pathKeys)
|
||||
for _, name := range heldNames(t) {
|
||||
if _, isPath := t.pathLevels[name]; isPath && isRef(name) {
|
||||
if _, isPath := t.pathKeys[name]; isPath && isRef(name) {
|
||||
continue // held for the render: drawCheck compares every read of it, by path, across the render and its groups
|
||||
}
|
||||
if err := checkNameHeld(t, name, readers); err != nil {
|
||||
@@ -36,8 +36,8 @@ func heldNames(t *template) []string {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Slice(names, func(i, j int) bool {
|
||||
_, pi := t.pathLevels[names[i]]
|
||||
_, pj := t.pathLevels[names[j]]
|
||||
_, pi := t.pathKeys[names[i]]
|
||||
_, pj := t.pathKeys[names[j]]
|
||||
if pi != pj {
|
||||
return !pi
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func heldNames(t *template) []string {
|
||||
// produces.
|
||||
func heldNodes(t *template, name string, readers []reader) map[node]bool {
|
||||
held := map[node]bool{}
|
||||
if _, isPath := t.pathLevels[name]; !isPath {
|
||||
if _, isPath := t.pathKeys[name]; !isPath {
|
||||
operandDraw(t.head(name), held)
|
||||
return held
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func checkNameHeld(t *template, name string, readers []reader) error {
|
||||
if len(held) == 0 {
|
||||
return nil // a fixed head holds nothing to reach
|
||||
}
|
||||
reader, isPath := t.pathLevels[name]
|
||||
reader, isPath := t.pathKeys[name]
|
||||
seen := map[node]bool{}
|
||||
for _, e := range renderEdges(t) {
|
||||
if splitArm(e.label, t.refs).key == name || !renders(e.to, held, seen) {
|
||||
@@ -177,8 +177,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
|
||||
// depend on where the tokens sit.
|
||||
// docs/decisions.md#a-bare-reference-draws-each-time-a-reference-path-is-held
|
||||
func checkNoOverlap(ops []op, pathLevels map[string]string) error {
|
||||
names := levelReaders(ops, pathLevels)
|
||||
func checkNoOverlap(ops []op, pathKeys map[string]string) error {
|
||||
names := pathKeyReaders(ops, pathKeys)
|
||||
// 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.
|
||||
sort.SliceStable(names, func(i, j int) bool { return names[i].path < names[j].path })
|
||||
@@ -192,23 +192,23 @@ func checkNoOverlap(ops []op, pathLevels map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// reader is one way a format reaches a path level: as written, by its path,
|
||||
// reader is one way a format reaches a path key: as written, by its path,
|
||||
// and how to name it.
|
||||
type reader struct{ spelling, path, label string }
|
||||
|
||||
// levelReaders lists every way a format reaches a sibling path level, in the order the
|
||||
// pathKeyReaders lists every way a format reaches a sibling path key, in the order the
|
||||
// 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.
|
||||
func levelReaders(ops []op, pathLevels map[string]string) []reader {
|
||||
func pathKeyReaders(ops []op, pathKeys map[string]string) []reader {
|
||||
var names []reader
|
||||
for _, o := range ops {
|
||||
for _, a := range o.operands {
|
||||
if _, isLevel := pathLevels[a.key]; isLevel && !isRef(a.key) {
|
||||
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) {
|
||||
names = append(names, reader{a.spelling, a.path, fmt.Sprintf("%s operand %q", o.fn, a.spelling)})
|
||||
}
|
||||
}
|
||||
for _, a := range o.arms {
|
||||
if _, isLevel := pathLevels[a.key]; isLevel && !isRef(a.key) {
|
||||
if _, isPathKey := pathKeys[a.key]; isPathKey && !isRef(a.key) {
|
||||
names = append(names, reader{a.spelling, a.path, "token {" + a.spelling + "}"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ type template struct {
|
||||
// Filled by `template.compileFormat` at link, from `template.tokens` and `template.refs`:
|
||||
ops []op // what expand walks
|
||||
grow int // minimum output size, to size the render buffer
|
||||
// pathLevels maps each field the format addresses by dotted path to one path token
|
||||
// pathKeys maps each key a dotted path in the format starts from to one path token
|
||||
// reading it, which is the half of an overlap the fences name. nil when the
|
||||
// format takes no path.
|
||||
pathLevels map[string]string
|
||||
// held is every name drawn once per expansion: the path levels above, plus the
|
||||
pathKeys map[string]string
|
||||
// held is every name drawn once per expansion: the path keys above, plus the
|
||||
// siblings a {calc()} reads. nil when the format holds nothing (see expand).
|
||||
held map[string]bool
|
||||
heldLocal bool // some held name is kept by the expansion itself, so expand makes its hold
|
||||
@@ -224,8 +224,8 @@ func (t *template) fixedText() (string, bool) {
|
||||
// compiled reads.
|
||||
func (t *template) compileFormat() error {
|
||||
c := compileOps(t.tokens, t.refs)
|
||||
t.ops, t.grow, t.pathLevels, t.held, t.heldLocal = c.ops, c.grow, c.pathLevels, c.held, c.heldLocal
|
||||
if err := checkNoOverlap(t.ops, t.pathLevels); err != nil {
|
||||
t.ops, t.grow, t.pathKeys, t.held, t.heldLocal = c.ops, c.grow, c.pathKeys, c.held, c.heldLocal
|
||||
if err := checkNoOverlap(t.ops, t.pathKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkNoRepeatedRead(c)
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ func eachTemplate(root map[string]node, fn func(folder []string, path string, t
|
||||
|
||||
// resolveCategory walks a dotted path through the folders to the category it
|
||||
// names, returning that category's segments, the node, and the tail left to read into it.
|
||||
func resolveCategory(root map[string]node, segments []string) (category []string, target node, tail []string, err error) {
|
||||
func resolveCategory(root map[string]node, segments []string) (categorySegs []string, target node, tail []string, err error) {
|
||||
var n node = &folder{children: root}
|
||||
i := 0
|
||||
for ; i < len(segments); i++ {
|
||||
|
||||
@@ -226,7 +226,7 @@ func (t *table) bindOptions(o tableOptionValues) error {
|
||||
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")
|
||||
}
|
||||
if err := t.mapKeys(); err != nil {
|
||||
if err := t.proveKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.proveNamesInsideParent(); err != nil {
|
||||
@@ -251,8 +251,8 @@ func (t *table) proveNamesInsideParent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapKeys proves every key names one row, and keeps the map a link is proved by.
|
||||
func (t *table) mapKeys() error {
|
||||
// proveKeys proves every key names one row, and keeps the map a link is proved by.
|
||||
func (t *table) proveKeys() error {
|
||||
if t.keyIndex < 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -488,13 +488,13 @@ func pickCum(s *session, cum []float64) int {
|
||||
|
||||
// drawUnder picks a row among those linked to parent row pr.
|
||||
func (t *table) drawUnder(s *session, pr int) int {
|
||||
ix := t.builtLookup()
|
||||
lookup := t.builtLookup()
|
||||
k := t.parentT.cell(pr, t.parentT.keyIndex)
|
||||
rows := ix.rowsByParent[k]
|
||||
if ix.childCum == nil {
|
||||
rows := lookup.rowsByParent[k]
|
||||
if lookup.childCum == nil {
|
||||
return rows[s.IntN(len(rows))]
|
||||
}
|
||||
return rows[pickCum(s, ix.childCum[k])]
|
||||
return rows[pickCum(s, lookup.childCum[k])]
|
||||
}
|
||||
|
||||
// drawIn is the render's row of t: the one pinned in p, else one drawn inside the nearest
|
||||
|
||||
Reference in New Issue
Block a user