Answer architecture round 1: List's walk and join sit in path.go, a compiled path's head is named head, every pick key comes from levelKeys, and readsColumn names all four readers
This commit is contained in:
@@ -982,7 +982,6 @@ 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: direct descents only
|
|
||||||
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
|
||||||
@@ -991,7 +990,7 @@ name.go names: {x as n} bindings, the scopes they live in, the reads the
|
|||||||
namefence.go the load fences over names
|
namefence.go the load fences over names
|
||||||
pick.go a render's named picks: the key of each level a pick keeps a draw at, built at load and read at render, the frame of each name scope rendering, the memo a pick keeps, and the reads kept under it
|
pick.go a render's named picks: the key of each level a pick keeps a draw at, built at load and read at render, the frame of each name scope rendering, the memo a pick keeps, and the reads kept under it
|
||||||
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
|
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, proving a path reaches a node, and the sub-paths every variant of a choice carries
|
path.go the dotted-path walks: the paths List advertises, the sub-paths every variant of a choice carries, proving a path reaches a node, and drawing it
|
||||||
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
|
||||||
struct.go structs: FakeStruct, fake tags as one record's columns, and a field's Go type as its column's datatype
|
struct.go structs: FakeStruct, fake tags as one record's columns, and a field's Go type as its column's datatype
|
||||||
@@ -1001,7 +1000,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, and join, two dotted paths joined
|
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, a template's declared datatype, a column's datatype, and a format that only reads a column
|
datatype.go column datatypes: DataType, a template's declared datatype, a column's datatype, and a format that only reads a column
|
||||||
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
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ func splitArm(name string, refs map[string]resolvedRef) arm {
|
|||||||
if resolved {
|
if resolved {
|
||||||
head = r.head
|
head = r.head
|
||||||
}
|
}
|
||||||
return arm{spelling: name, head: head, levels: []pickKey{pickKey(head)}}
|
return arm{spelling: name, head: head, levels: levelKeys([]string{head}, 1)}
|
||||||
}
|
}
|
||||||
return pathArm(name, r.head, r.tail)
|
return pathArm(name, r.head, r.tail)
|
||||||
}
|
}
|
||||||
segs, err := grammar.SplitPath(name)
|
segs, err := grammar.SplitPath(name)
|
||||||
if err != nil || len(segs) == 1 {
|
if err != nil || len(segs) == 1 {
|
||||||
return arm{spelling: name, head: name, levels: []pickKey{pickKey(name)}}
|
return arm{spelling: name, head: name, levels: levelKeys([]string{name}, 1)}
|
||||||
}
|
}
|
||||||
return pathArm(name, segs[0], segs[1:])
|
return pathArm(name, segs[0], segs[1:])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ package fejkdata
|
|||||||
// `namedPick`, `pickFrame`.
|
// `namedPick`, `pickFrame`.
|
||||||
// - memo — what a named pick keeps its draws in, the variant drawn at each level
|
// - memo — what a named pick keeps its draws in, the variant drawn at each level
|
||||||
// and the value each path read: `drawMemo`.
|
// and the value each path read: `drawMemo`.
|
||||||
// - level — a prefix of a read's path, from where it starts to its leaf: `pickKey`.
|
// - level — `pickKey`.
|
||||||
// - pick key — the key a memo keeps a level's draw under: `pickKey`.
|
// - pick key — `pickKey`.
|
||||||
// - addressed key — a pick key some read of a name lands on or passes: `pickKey`.
|
// - addressed key — `pickKey`.
|
||||||
// - pin — fixing which row of a table one path, or one named pick, uses:
|
// - pin — fixing which row of a table one path, or one named pick, uses:
|
||||||
// `pinSet`, `namedPick.pins`, `table.followStep`.
|
// `pinSet`, `namedPick.pins`, `table.followStep`.
|
||||||
// - link — a table's tie to its parent table, each row to its parent row:
|
// - link — a table's tie to its parent table, each row to its parent row:
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Comprehension history
|
||||||
|
|
||||||
|
## 2026-10-09T08:59:19Z, PR #190 at 17f732a, against ee4b40b
|
||||||
|
|
||||||
|
Ruling: better
|
||||||
|
|
||||||
|
| Seat | Vote |
|
||||||
|
|---|---|
|
||||||
|
| Mid A | better |
|
||||||
|
| Inherited architect | better |
|
||||||
|
|
||||||
|
Mid A, decided by:
|
||||||
|
|
||||||
|
- `pick.go:9` `pickKey`: the definition sits on the type, beside `levelKeys`, `addressedKeys`, `keptInPick` and `readUnder`, where four files held them
|
||||||
|
- `render.go:155` `readField`: one named predicate, `keptInPick`, states the whole keep rule
|
||||||
|
- `datatype.go:217` `columnRead`: beside the code that reads it
|
||||||
|
|
||||||
|
Inherited architect, decided by:
|
||||||
|
|
||||||
|
- `pick.go:9` `pickKey`: the 3am path for a named pick now ends in one file, with its worked example
|
||||||
|
- `render.go:155` `readField`: `keptInPick` holds its own nil and reference checks
|
||||||
|
- `path.go:72` `compiledPath`: the field no longer reuses the vocabulary word level
|
||||||
@@ -94,8 +94,7 @@ type renderEdge struct {
|
|||||||
label string
|
label string
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderEdges lists the children rendering n recurses into, mirroring render: a
|
// renderEdges lists the children rendering n recurses into, mirroring render.
|
||||||
// choice's items, and a template's field/reference tokens plus its operands.
|
|
||||||
func renderEdges(n node) []renderEdge {
|
func renderEdges(n node) []renderEdge {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *choice:
|
case *choice:
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
package fejkdata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/larvit/fejkdata/internal/invariant"
|
|
||||||
)
|
|
||||||
|
|
||||||
// paths lists the dot paths addressable from n, relative to it, where "" is n
|
|
||||||
// itself. A folder has no value of its own, so it contributes only its children's.
|
|
||||||
// With intoRepeats, it also lists the paths below a level carrying a repeat, so a path
|
|
||||||
// there meets the repeat's refusal.
|
|
||||||
func paths(n node, intoRepeats bool) []string {
|
|
||||||
switch n := n.(type) {
|
|
||||||
case *folder:
|
|
||||||
var out []string
|
|
||||||
for _, name := range sortedNames(n.children) {
|
|
||||||
out = appendUnder(out, name, paths(n.children[name], intoRepeats))
|
|
||||||
}
|
|
||||||
for _, name := range sortedNames(n.unloaded) {
|
|
||||||
out = appendUnder(out, name, n.unloaded[name].paths)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
case *template:
|
|
||||||
out := []string{""}
|
|
||||||
if n.repeat > 1 && !intoRepeats {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
for _, name := range sortedNames(n.fields) {
|
|
||||||
out = appendUnder(out, name, paths(n.fields[name], intoRepeats))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
case *table:
|
|
||||||
return tablePaths(n, intoRepeats)
|
|
||||||
case *tableColumn, *tableRow, *nullItem:
|
|
||||||
return []string{""}
|
|
||||||
case *choice:
|
|
||||||
shared := n.shared
|
|
||||||
if !intoRepeats {
|
|
||||||
shared = sharedPaths(n.items, false)
|
|
||||||
}
|
|
||||||
return append([]string{""}, sortedNames(shared)...)
|
|
||||||
default:
|
|
||||||
panic(invariant.Broken("paths has no case for node %T", n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendUnder(out []string, name string, ps []string) []string {
|
|
||||||
for _, p := range ps {
|
|
||||||
out = append(out, join(name, p))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// tablePaths is a table's columns, then each table linked to it under its name: the
|
|
||||||
// direct descents, a step at a time.
|
|
||||||
func tablePaths(t *table, intoRepeats bool) []string {
|
|
||||||
out := append([]string{""}, t.rows.Header()...)
|
|
||||||
sort.Strings(out[1:])
|
|
||||||
for _, c := range t.rows.Children() {
|
|
||||||
out = appendUnder(out, c.Segment(), paths(c.Owner(), intoRepeats))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,8 @@ type template struct {
|
|||||||
refs templateRefs
|
refs templateRefs
|
||||||
compiled formatOps
|
compiled formatOps
|
||||||
// readsColumn is set when the format only reads one reference or name, and that read is a
|
// readsColumn is set when the format only reads one reference or name, and that read is a
|
||||||
// record's column. renderLeaf, proveColumnItem and itemDatatype each read it: change them together.
|
// record's column. checkColumns, itemDatatype, proveColumnItem and renderLeaf each read it: change
|
||||||
|
// them together.
|
||||||
readsColumn *columnRead
|
readsColumn *columnRead
|
||||||
|
|
||||||
// Filled by `settleRecords`, once the load refused every cycle:
|
// Filled by `settleRecords`, once the load refused every cycle:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package fejkdata
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/larvit/fejkdata/internal/drawstate"
|
"github.com/larvit/fejkdata/internal/drawstate"
|
||||||
@@ -68,10 +69,10 @@ const (
|
|||||||
// compiledPath walks a path and proves it reaches a node whichever way the draws go: every
|
// compiledPath walks a path and proves it reaches a node whichever way the draws go: every
|
||||||
// variant of a choice carries the rest of it, and is walked, a selector names a row inside the
|
// variant of a choice carries the rest of it, and is walked, a selector names a row inside the
|
||||||
// rows selected before it, and no level read carries a repeat. Once run, it holds the steps a
|
// rows selected before it, and no level read carries a repeat. Once run, it holds the steps a
|
||||||
// draw takes, and every leaf the path may render; label names the head in its errors.
|
// draw takes, and every leaf the path may render; head names where it starts in its errors.
|
||||||
type compiledPath struct {
|
type compiledPath struct {
|
||||||
pins pinSet
|
pins pinSet
|
||||||
label string
|
head string
|
||||||
tail []string
|
tail []string
|
||||||
steps []pathStep
|
steps []pathStep
|
||||||
leaves []node
|
leaves []node
|
||||||
@@ -88,7 +89,7 @@ func (w *compiledPath) walk(n node, tail []string) (node, error) {
|
|||||||
return w.walkEvery(c, at.tail)
|
return w.walkEvery(c, at.tail)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if w.steps, err = compileStep(&at, w.tail, w.label, w.steps, &w.pins); err != nil {
|
if w.steps, err = compileStep(&at, w.tail, w.head, w.steps, &w.pins); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,8 +113,8 @@ func (p pathPos) more() bool { return len(p.tail) > 0 || p.fromRow }
|
|||||||
|
|
||||||
// compileStep takes the first step of at.tail from at.n, which must not be a choice, moves at past
|
// compileStep takes the first step of at.tail from at.n, which must not be a choice, moves at past
|
||||||
// it, and appends the step to steps: a field, or the route through a table. whole is the full
|
// it, and appends the step to steps: a field, or the route through a table. whole is the full
|
||||||
// path; label is the prefix an error puts before it.
|
// path; head is the prefix an error puts before it.
|
||||||
func compileStep(at *pathPos, whole []string, label string, steps []pathStep, pins *pinSet) ([]pathStep, error) {
|
func compileStep(at *pathPos, whole []string, head string, steps []pathStep, pins *pinSet) ([]pathStep, error) {
|
||||||
i := len(whole) - len(at.tail)
|
i := len(whole) - len(at.tail)
|
||||||
switch x := at.n.(type) {
|
switch x := at.n.(type) {
|
||||||
case *table:
|
case *table:
|
||||||
@@ -125,7 +126,7 @@ func compileStep(at *pathPos, whole []string, label string, steps []pathStep, pi
|
|||||||
return routeSteps(steps, pins, x, r, i)
|
return routeSteps(steps, pins, x, r, i)
|
||||||
case *template:
|
case *template:
|
||||||
if x.repeat > 1 && !grammar.IsSelector(at.tail[0]) {
|
if x.repeat > 1 && !grammar.IsSelector(at.tail[0]) {
|
||||||
return steps, repeatLevelError(join(label, strings.Join(whole[:i], ".")))
|
return steps, repeatLevelError(join(head, grammar.JoinSegments(whole[:i])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next, err := childNamed(at.n, at.tail[0])
|
next, err := childNamed(at.n, at.tail[0])
|
||||||
@@ -222,6 +223,73 @@ func drawVariant(s *drawstate.State, c *choice, memo *drawMemo, levels []pickKey
|
|||||||
return memo.variantOf(s, c, levels[at])
|
return memo.variantOf(s, c, levels[at])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paths lists the dot paths addressable from n, relative to it, where "" is n
|
||||||
|
// itself. A folder has no value of its own, so it contributes only its children's.
|
||||||
|
// With intoRepeats, it also lists the paths below a level carrying a repeat, so a path
|
||||||
|
// there meets the repeat's refusal.
|
||||||
|
func paths(n node, intoRepeats bool) []string {
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *folder:
|
||||||
|
var out []string
|
||||||
|
for _, name := range sortedNames(n.children) {
|
||||||
|
out = appendUnder(out, name, paths(n.children[name], intoRepeats))
|
||||||
|
}
|
||||||
|
for _, name := range sortedNames(n.unloaded) {
|
||||||
|
out = appendUnder(out, name, n.unloaded[name].paths)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
case *template:
|
||||||
|
out := []string{""}
|
||||||
|
if n.repeat > 1 && !intoRepeats {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
for _, name := range sortedNames(n.fields) {
|
||||||
|
out = appendUnder(out, name, paths(n.fields[name], intoRepeats))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
case *table:
|
||||||
|
return tablePaths(n, intoRepeats)
|
||||||
|
case *tableColumn, *tableRow, *nullItem:
|
||||||
|
return []string{""}
|
||||||
|
case *choice:
|
||||||
|
shared := n.shared
|
||||||
|
if !intoRepeats {
|
||||||
|
shared = sharedPaths(n.items, false)
|
||||||
|
}
|
||||||
|
return append([]string{""}, sortedNames(shared)...)
|
||||||
|
default:
|
||||||
|
panic(invariant.Broken("paths has no case for node %T", n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendUnder(out []string, name string, ps []string) []string {
|
||||||
|
for _, p := range ps {
|
||||||
|
out = append(out, join(name, p))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// tablePaths is a table's columns, then each table linked to it under its name: the
|
||||||
|
// direct descents, a step at a time.
|
||||||
|
func tablePaths(t *table, intoRepeats bool) []string {
|
||||||
|
out := append([]string{""}, t.rows.Header()...)
|
||||||
|
sort.Strings(out[1:])
|
||||||
|
for _, c := range t.rows.Children() {
|
||||||
|
out = appendUnder(out, c.Segment(), paths(c.Owner(), intoRepeats))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func join(prefix, name string) string {
|
||||||
|
switch {
|
||||||
|
case prefix == "":
|
||||||
|
return name
|
||||||
|
case name == "":
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
return prefix + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
// sharedPaths is the sub-paths every item carries — the only ones a path may step
|
// sharedPaths is the sub-paths every item carries — the only ones a path may step
|
||||||
// through a choice to reach. It intersects, bailing as soon as the set
|
// through a choice to reach. It intersects, bailing as soon as the set
|
||||||
// is empty, which is immediate for a choice of plain strings.
|
// is empty, which is immediate for a choice of plain strings.
|
||||||
@@ -271,8 +339,8 @@ func unreachableInChoice(c *choice, want string) error {
|
|||||||
return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered)
|
return fmt.Errorf("not every variant of this %d-way choice carries %q; all carry %v", len(c.items), want, offered)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provePath(n node, tail []string, label string) error {
|
func provePath(n node, tail []string, head string) error {
|
||||||
_, err := (&compiledPath{label: label, tail: tail}).run(n)
|
_, err := (&compiledPath{head: head, tail: tail}).run(n)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -303,4 +303,4 @@ func renderRecord(s *drawstate.State, t *template, env renderEnv) *Record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wholeLevels is the levels of a read of a whole name, as compileArm compiles {n}.
|
// wholeLevels is the levels of a read of a whole name, as compileArm compiles {n}.
|
||||||
var wholeLevels = []pickKey{""}
|
var wholeLevels = levelKeys(nil, 0)
|
||||||
|
|||||||
@@ -10,13 +10,3 @@ func sortedNames[V any](m map[string]V) []string {
|
|||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
func join(prefix, name string) string {
|
|
||||||
switch {
|
|
||||||
case prefix == "":
|
|
||||||
return name
|
|
||||||
case name == "":
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
return prefix + "." + name
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user