Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33
@@ -118,7 +118,7 @@ func (t *template) compileArm(name string, targets map[*nameBinding]nameTarget)
|
|||||||
}
|
}
|
||||||
target := targets[b]
|
target := targets[b]
|
||||||
full := append(target.tail[:len(target.tail):len(target.tail)], a.tail...)
|
full := append(target.tail[:len(target.tail):len(target.tail)], a.tail...)
|
||||||
if err := checkPathReaches(target.start, full, a.head); err != nil {
|
if err := provePath(target.start, full, a.head); err != nil {
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
w := compilePath(target.start, full)
|
w := compilePath(target.start, full)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ package fejkdata
|
|||||||
// and {p.first} are read, the {first} that {p} renders has the key "first" under {p}'s
|
// and {p.first} are read, the {first} that {p} renders has the key "first" under {p}'s
|
||||||
// key "". {p.first} addresses "first", so both read one draw.
|
// key "". {p.first} addresses "first", so both read one draw.
|
||||||
// - 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.drawStep`.
|
// `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:
|
||||||
// `table.linkParent`.
|
// `table.linkParent`.
|
||||||
// - family — a table and every table reaching it through a chain of parent
|
// - family — a table and every table reaching it through a chain of parent
|
||||||
|
|||||||
+5
-5
@@ -798,12 +798,12 @@ the direct chain is the one a reader can predict from the tables' parents.
|
|||||||
2026-09-30, larv-review on the comprehension round the maintainer approved on 2026-09-29;
|
2026-09-30, larv-review on the comprehension round the maintainer approved on 2026-09-29;
|
||||||
replaces "The path walks are separate loops".
|
replaces "The path walks are separate loops".
|
||||||
Goals 3 and 13: `drawSteps` draws every path from its steps, compiled when templates resolve, by
|
Goals 3 and 13: `drawSteps` draws every path from its steps, compiled when templates resolve, by
|
||||||
`pathCheck` or, for a caller's path, by `probePath` into a stack buffer. Both take each
|
`compiledPath` or, for a caller's path, by `callerPathSteps` into a stack buffer. Both take each
|
||||||
step through `takeStep`, and differ only at a choice: the check walks every variant, the
|
step through `compileStep`, and differ only at a choice: `compiledPath` walks every variant,
|
||||||
probe the first. On 2026-09-29 one loop over all three walks, switching on a mode field,
|
`callerPathSteps` the first. On 2026-09-29 one loop over all three walks, switching on a mode field,
|
||||||
leaked the check's leaves and errors with the draw's pins, arm and memo, since Go tracks a
|
leaked `compiledPath`'s leaves and errors with the draw's pins, arm and memo, since Go tracks a
|
||||||
struct's fields as one, and a repeat of a reference path rose from 66 to 106 allocations.
|
struct's fields as one, and a repeat of a reference path rose from 66 to 106 allocations.
|
||||||
`drawSteps` and `takeStep` take their state as parameters and a step holds no node, so no
|
`drawSteps` and `compileStep` take their state as parameters and a step holds no node, so no
|
||||||
benchmark gained an allocation. Valid while Go's escape analysis tracks a struct's fields
|
benchmark gained an allocation. Valid while Go's escape analysis tracks a struct's fields
|
||||||
as one.
|
as one.
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ func checkArm(name string, fields map[string]node, wholeToken bool) (missing boo
|
|||||||
}
|
}
|
||||||
return true, fmt.Errorf("no field %q", a.head)
|
return true, fmt.Errorf("no field %q", a.head)
|
||||||
}
|
}
|
||||||
if err := checkPathReaches(field, a.tail, a.head); err != nil {
|
if err := provePath(field, a.tail, a.head); err != nil {
|
||||||
return false, fmt.Errorf("field %q: %w", a.head, err)
|
return false, fmt.Errorf("field %q: %w", a.head, err)
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/larvit/fejkdata/internal/invariant"
|
"github.com/larvit/fejkdata/internal/invariant"
|
||||||
)
|
)
|
||||||
|
|
||||||
// stepInto is what seg names under n: a folder's entry or a template's field.
|
// childNamed is what seg names under n: a folder's entry or a template's field.
|
||||||
func stepInto(n node, seg string) (node, error) {
|
func childNamed(n node, seg string) (node, error) {
|
||||||
switch {
|
switch {
|
||||||
case grammar.IsSelector(seg):
|
case grammar.IsSelector(seg):
|
||||||
return nil, fmt.Errorf("%s is not a table, so it has no row to select", grammar.SelectorOf(seg))
|
return nil, fmt.Errorf("%s is not a table, so it has no row to select", grammar.SelectorOf(seg))
|
||||||
@@ -37,7 +37,7 @@ func stepInto(n node, seg string) (node, error) {
|
|||||||
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", seg, n.name())
|
return nil, fmt.Errorf("no field %q: %q is a column, and a cell holds no fields", seg, n.name())
|
||||||
case *nullItem:
|
case *nullItem:
|
||||||
default:
|
default:
|
||||||
panic(invariant.Broken("stepInto has no case for node %T", n))
|
panic(invariant.Broken("childNamed has no case for node %T", n))
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no field %q", seg)
|
return nil, fmt.Errorf("no field %q", seg)
|
||||||
}
|
}
|
||||||
@@ -65,11 +65,11 @@ const (
|
|||||||
stepParent // up to the table's parent, at the row its own row links to
|
stepParent // up to the table's parent, at the row its own row links to
|
||||||
)
|
)
|
||||||
|
|
||||||
// pathCheck proves a path reaches a node whichever way the draws go: every variant of a
|
// compiledPath is a path proved to reach 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
|
// 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. It compiles the steps
|
// rows selected before it, and no level read carries a repeat. It holds the steps
|
||||||
// a draw takes, and every leaf the path may render; level names the head in its errors.
|
// a draw takes, and every leaf the path may render; level names the head in its errors.
|
||||||
type pathCheck struct {
|
type compiledPath struct {
|
||||||
pins pinSet
|
pins pinSet
|
||||||
level string
|
level string
|
||||||
tail []string
|
tail []string
|
||||||
@@ -77,18 +77,18 @@ type pathCheck struct {
|
|||||||
leaves []node
|
leaves []node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *pathCheck) run(n node) (node, error) {
|
func (w *compiledPath) run(n node) (node, error) {
|
||||||
return w.walk(n, w.tail)
|
return w.walk(n, w.tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *pathCheck) walk(n node, tail []string) (node, error) {
|
func (w *compiledPath) walk(n node, tail []string) (node, error) {
|
||||||
at := pathPos{n: n, tail: tail}
|
at := pathPos{n: n, tail: tail}
|
||||||
for at.more() {
|
for at.more() {
|
||||||
if c, ok := at.n.(*choice); ok {
|
if c, ok := at.n.(*choice); ok {
|
||||||
return w.walkEvery(c, at.tail)
|
return w.walkEvery(c, at.tail)
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if w.steps, err = takeStep(&at, w.tail, w.level, w.steps, &w.pins); err != nil {
|
if w.steps, err = compileStep(&at, w.tail, w.level, w.steps, &w.pins); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,35 +100,35 @@ func repeatLevelError(level string) error {
|
|||||||
return fmt.Errorf("the level %q carries a repeat, so a path cannot read one draw of it; read %q whole", level, level)
|
return fmt.Errorf("the level %q carries a repeat, so a path cannot read one draw of it; read %q whole", level, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathPos is where a walk stands: the node reached, the tail left to walk from it, and route's
|
// pathPos is where a walk stands: the node reached, the tail left to walk from it, and fromRow,
|
||||||
// descended flag for it.
|
// whether the walk entered n, a table, from a row of a table linked to it.
|
||||||
type pathPos struct {
|
type pathPos struct {
|
||||||
n node
|
n node
|
||||||
tail []string
|
tail []string
|
||||||
descended bool
|
fromRow bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p pathPos) more() bool { return len(p.tail) > 0 || p.descended }
|
func (p pathPos) more() bool { return len(p.tail) > 0 || p.fromRow }
|
||||||
|
|
||||||
// takeStep 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; level is the prefix an error puts before it.
|
// path; level is the prefix an error puts before it.
|
||||||
func takeStep(at *pathPos, whole []string, level string, steps []pathStep, pins *pinSet) ([]pathStep, error) {
|
func compileStep(at *pathPos, whole []string, level 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:
|
||||||
r, err := x.route(at.tail, at.descended)
|
r, err := x.route(at.tail, at.fromRow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return steps, err
|
return steps, err
|
||||||
}
|
}
|
||||||
*at = pathPos{n: r.next, tail: r.rest, descended: r.descends}
|
*at = pathPos{n: r.next, tail: r.rest, fromRow: r.nextLinked}
|
||||||
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(level, strings.Join(whole[:i], ".")))
|
return steps, repeatLevelError(join(level, strings.Join(whole[:i], ".")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next, err := stepInto(at.n, at.tail[0])
|
next, err := childNamed(at.n, at.tail[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return steps, err
|
return steps, err
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ func takeStep(at *pathPos, whole []string, level string, steps []pathStep, pins
|
|||||||
|
|
||||||
// walkEvery walks every variant and keeps the first one's steps: every variant carries
|
// walkEvery walks every variant and keeps the first one's steps: every variant carries
|
||||||
// the rest of the path, so each compiles to the same steps past the choice.
|
// the rest of the path, so each compiles to the same steps past the choice.
|
||||||
func (w *pathCheck) walkEvery(c *choice, tail []string) (node, error) {
|
func (w *compiledPath) walkEvery(c *choice, tail []string) (node, error) {
|
||||||
if err := carriedByAll(c, tail); err != nil {
|
if err := carriedByAll(c, tail); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -159,10 +159,10 @@ func (w *pathCheck) walkEvery(c *choice, tail []string) (node, error) {
|
|||||||
return last, nil
|
return last, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// probePath proves a path reaches a node without drawing, appending to steps the steps a
|
// callerPathSteps proves a path reaches a node without drawing, appending to steps the steps a
|
||||||
// draw takes. Where pathCheck walks every variant of a choice, it walks the first,
|
// draw takes. Where compiledPath walks every variant of a choice, it walks the first,
|
||||||
// which carriedByAll lets stand for all.
|
// which carriedByAll lets stand for all.
|
||||||
func probePath(n node, tail []string, steps []pathStep) ([]pathStep, error) {
|
func callerPathSteps(n node, tail []string, steps []pathStep) ([]pathStep, error) {
|
||||||
var pins pinSet
|
var pins pinSet
|
||||||
for at := (pathPos{n: n, tail: tail}); at.more(); {
|
for at := (pathPos{n: n, tail: tail}); at.more(); {
|
||||||
if c, ok := at.n.(*choice); ok {
|
if c, ok := at.n.(*choice); ok {
|
||||||
@@ -173,7 +173,7 @@ func probePath(n node, tail []string, steps []pathStep) ([]pathStep, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if steps, err = takeStep(&at, tail, "", steps, &pins); err != nil {
|
if steps, err = compileStep(&at, tail, "", steps, &pins); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ func drawSteps(s *drawstate.State, n node, steps []pathStep, pins *pinSet, memo
|
|||||||
}
|
}
|
||||||
if st.kind == stepField {
|
if st.kind == stepField {
|
||||||
var err error
|
var err error
|
||||||
if n, err = stepInto(n, st.name); err != nil {
|
if n, err = childNamed(n, st.name); err != nil {
|
||||||
panic(invariant.Broken("step %d: %v; a path's steps should have been compiled from the node they are drawn from", st.at, err))
|
panic(invariant.Broken("step %d: %v; a path's steps should have been compiled from the node they are drawn from", st.at, err))
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -210,7 +210,7 @@ func drawSteps(s *drawstate.State, n node, steps []pathStep, pins *pinSet, memo
|
|||||||
case st.kind == stepChild && climbed != nil:
|
case st.kind == stepChild && climbed != nil:
|
||||||
pins, climbed = memo.stepDownPins(pins, climbed, levels, st.at), nil
|
pins, climbed = memo.stepDownPins(pins, climbed, levels, st.at), nil
|
||||||
}
|
}
|
||||||
n = t.drawStep(s, st, pins)
|
n = t.followStep(s, st, pins)
|
||||||
}
|
}
|
||||||
return n, pins
|
return n, pins
|
||||||
}
|
}
|
||||||
@@ -241,14 +241,14 @@ 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 checkPathReaches(n node, tail []string, level string) error {
|
func provePath(n node, tail []string, level string) error {
|
||||||
_, err := (&pathCheck{level: level, tail: tail}).run(n)
|
_, err := (&compiledPath{level: level, tail: tail}).run(n)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// compilePath compiles a path checkPathReaches proved.
|
// compilePath compiles a path provePath proved.
|
||||||
func compilePath(n node, tail []string) pathCheck {
|
func compilePath(n node, tail []string) compiledPath {
|
||||||
w := pathCheck{tail: tail}
|
w := compiledPath{tail: tail}
|
||||||
if _, err := w.run(n); err != nil {
|
if _, err := w.run(n); err != nil {
|
||||||
panic(invariant.Broken("%s: %v; the path should have been proved before it was compiled", grammar.JoinSegments(tail), err))
|
panic(invariant.Broken("%s: %v; the path should have been proved before it was compiled", grammar.JoinSegments(tail), err))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ func TestPathCheckStopsAtAMissingSegment(t *testing.T) {
|
|||||||
if leaves := compilePath(n, []string{"a", "b"}).leaves; len(leaves) != 1 {
|
if leaves := compilePath(n, []string{"a", "b"}).leaves; len(leaves) != 1 {
|
||||||
t.Fatalf("compilePath(a.b).leaves = %v, want the one leaf", leaves)
|
t.Fatalf("compilePath(a.b).leaves = %v, want the one leaf", leaves)
|
||||||
}
|
}
|
||||||
w := &pathCheck{tail: []string{"a", "nope", "deeper"}}
|
w := &compiledPath{tail: []string{"a", "nope", "deeper"}}
|
||||||
_, err := w.run(n)
|
_, err := w.run(n)
|
||||||
if err == nil || !strings.Contains(err.Error(), `no field "nope"`) {
|
if err == nil || !strings.Contains(err.Error(), `no field "nope"`) {
|
||||||
t.Errorf("walk(a.nope.deeper) = %v, want the missing segment named", err)
|
t.Errorf("walk(a.nope.deeper) = %v, want the missing segment named", err)
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ func (f *Generator) FakeRecord(path string) (*Record, error) {
|
|||||||
// tableRecord walks a path's tail from a table to the table whose row is the record, and that
|
// tableRecord walks a path's tail from a table to the table whose row is the record, and that
|
||||||
// row, selected or drawn.
|
// row, selected or drawn.
|
||||||
func tableRecord(s *drawstate.State, t *table, tail []string) (node, renderedRow, error) {
|
func tableRecord(s *drawstate.State, t *table, tail []string) (node, renderedRow, error) {
|
||||||
n, pins, err := descend(s, t, tail)
|
n, pins, err := drawCallerPath(s, t, tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, renderedRow{}, err
|
return nil, renderedRow{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ func (t *template) resolveRefs(folder []string, label, category string, root map
|
|||||||
if category != "" && head == "/"+category {
|
if category != "" && head == "/"+category {
|
||||||
return refs, fmt.Errorf("%s: reference {%s}: names the category it sits in; read a sibling field as a path, or move the shared value into its own category and reference that", label, name)
|
return refs, fmt.Errorf("%s: reference {%s}: names the category it sits in; read a sibling field as a path, or move the shared value into its own category and reference that", label, name)
|
||||||
}
|
}
|
||||||
if err := checkPathReaches(target, tail, head); err != nil {
|
if err := provePath(target, tail, head); err != nil {
|
||||||
return refs, fmt.Errorf("%s: reference {%s}: %w", label, name, err)
|
return refs, fmt.Errorf("%s: reference {%s}: %w", label, name, err)
|
||||||
}
|
}
|
||||||
refs.categories[head] = target
|
refs.categories[head] = target
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
return "", fmt.Errorf("fejkdata: %w", err)
|
return "", fmt.Errorf("fejkdata: %w", err)
|
||||||
}
|
}
|
||||||
f.loadShippedAt(segments)
|
f.loadShippedAt(segments)
|
||||||
n, pins, err := descend(f.drawState, &f.root, segments)
|
n, pins, err := drawCallerPath(f.drawState, &f.root, segments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("fejkdata: %s: %w", path, err)
|
return "", fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||||
}
|
}
|
||||||
@@ -33,11 +33,11 @@ func (f *Generator) Fake(path string) (string, error) {
|
|||||||
return render(f.drawState, n, env), nil
|
return render(f.drawState, n, env), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// descend walks a caller's path to the node it names, returning the rows its leaf renders in.
|
// drawCallerPath walks a caller's path to the node it names, returning the rows its leaf renders in.
|
||||||
func descend(s *drawstate.State, root node, segments []string) (node, pinSet, error) {
|
func drawCallerPath(s *drawstate.State, root node, segments []string) (node, pinSet, error) {
|
||||||
// docs/decisions.md#a-path-is-walked-once-without-drawing-before-it-is-walked-for-real
|
// docs/decisions.md#a-path-is-walked-once-without-drawing-before-it-is-walked-for-real
|
||||||
var buf [16]pathStep
|
var buf [16]pathStep
|
||||||
steps, err := probePath(root, segments, buf[:0])
|
steps, err := callerPathSteps(root, segments, buf[:0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, pinSet{}, err
|
return nil, pinSet{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ var nodeSwitchSkips = map[string][]string{
|
|||||||
"columnItems": {"folder", "table", "tableRow"},
|
"columnItems": {"folder", "table", "tableRow"},
|
||||||
"prove": {"folder"},
|
"prove": {"folder"},
|
||||||
"render": {"folder"},
|
"render": {"folder"},
|
||||||
"stepInto": {"choice", "table", "tableRow"},
|
"childNamed": {"choice", "table", "tableRow"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeSwitchesHandleEveryKind(t *testing.T) {
|
func TestNodeSwitchesHandleEveryKind(t *testing.T) {
|
||||||
@@ -116,7 +116,7 @@ func TestNodeSwitchesHandleEveryKind(t *testing.T) {
|
|||||||
render(engine(1).drawState, n, renderEnv{frames: &frames, row: renderedRow{tbl, 0}})
|
render(engine(1).drawState, n, renderEnv{frames: &frames, row: renderedRow{tbl, 0}})
|
||||||
},
|
},
|
||||||
"renderEdges": func(n node) { renderEdges(n) },
|
"renderEdges": func(n node) { renderEdges(n) },
|
||||||
"stepInto": func(n node) { _, _ = stepInto(n, "x") },
|
"childNamed": func(n node) { _, _ = childNamed(n, "x") },
|
||||||
} {
|
} {
|
||||||
mustPanic(t, name+" on an unhandled node", func() { call(&unhandledNode{}) })
|
mustPanic(t, name+" on an unhandled node", func() { call(&unhandledNode{}) })
|
||||||
for _, kind := range kinds {
|
for _, kind := range kinds {
|
||||||
|
|||||||
@@ -237,19 +237,19 @@ func (t *table) linkParent(siblings map[string]node) error {
|
|||||||
// tableRoute is how a path passes one table: the row it reads, by selector or
|
// tableRoute is how a path passes one table: the row it reads, by selector or
|
||||||
// drawn, what it goes on into, and whether that is a linked table.
|
// drawn, what it goes on into, and whether that is a linked table.
|
||||||
type tableRoute struct {
|
type tableRoute struct {
|
||||||
sel string
|
sel string
|
||||||
draw bool
|
draw bool
|
||||||
next node
|
next node
|
||||||
rest []string
|
rest []string
|
||||||
descends bool
|
nextLinked bool
|
||||||
up bool
|
up bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// route is how tail passes t. descended means the previous route stepped into t
|
// route is how tail passes t. fromRow means the previous route stepped into t
|
||||||
// from a row of an ancestor table, so t reads a row even where tail is empty; a
|
// from a row of an ancestor table, so t reads a row even where tail is empty; a
|
||||||
// table reached otherwise, with no selector and an empty tail, is left to a render's
|
// table reached otherwise, with no selector and an empty tail, is left to a render's
|
||||||
// own draw.
|
// own draw.
|
||||||
func (t *table) route(tail []string, descended bool) (tableRoute, error) {
|
func (t *table) route(tail []string, fromRow bool) (tableRoute, error) {
|
||||||
sel, tail, err := t.selector(tail)
|
sel, tail, err := t.selector(tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tableRoute{}, err
|
return tableRoute{}, err
|
||||||
@@ -258,14 +258,14 @@ func (t *table) route(tail []string, descended bool) (tableRoute, error) {
|
|||||||
if err := t.rows.ProveStepUp(tail[1:]); err != nil {
|
if err := t.rows.ProveStepUp(tail[1:]); err != nil {
|
||||||
return tableRoute{}, err
|
return tableRoute{}, err
|
||||||
}
|
}
|
||||||
return tableRoute{sel: sel, draw: sel == "", next: t.rows.Parent().Owner(), rest: tail[2:], descends: true, up: true}, nil
|
return tableRoute{sel: sel, draw: sel == "", next: t.rows.Parent().Owner(), rest: tail[2:], nextLinked: true, up: true}, nil
|
||||||
}
|
}
|
||||||
column, child, err := t.step(tail)
|
column, child, err := t.columnOrLinked(tail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tableRoute{}, err
|
return tableRoute{}, err
|
||||||
}
|
}
|
||||||
selected := sel != ""
|
selected := sel != ""
|
||||||
readsRow := selected || descended || len(tail) > 0
|
readsRow := selected || fromRow || len(tail) > 0
|
||||||
// A selector further down pins this table by ancestry; drawing first could draw a row the
|
// A selector further down pins this table by ancestry; drawing first could draw a row the
|
||||||
// selector is not inside.
|
// selector is not inside.
|
||||||
pinnedBelow := grammar.HasSelector(tail)
|
pinnedBelow := grammar.HasSelector(tail)
|
||||||
@@ -276,7 +276,7 @@ func (t *table) route(tail []string, descended bool) (tableRoute, error) {
|
|||||||
case len(tail) == 0:
|
case len(tail) == 0:
|
||||||
r.next = t.rowNode
|
r.next = t.rowNode
|
||||||
case child != nil:
|
case child != nil:
|
||||||
r.next, r.rest, r.descends = child, tail[1:], true
|
r.next, r.rest, r.nextLinked = child, tail[1:], true
|
||||||
default:
|
default:
|
||||||
r.next, r.rest = column, tail[1:]
|
r.next, r.rest = column, tail[1:]
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ func routeSteps(steps []pathStep, pins *pinSet, t *table, r tableRoute, at int)
|
|||||||
return steps, nil
|
return steps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) drawStep(s *drawstate.State, st pathStep, pins *pinSet) node {
|
func (t *table) followStep(s *drawstate.State, st pathStep, pins *pinSet) node {
|
||||||
switch st.kind {
|
switch st.kind {
|
||||||
case stepSelect:
|
case stepSelect:
|
||||||
if err := pins.PinRow(t.rows, st.row); err != nil {
|
if err := pins.PinRow(t.rows, st.row); err != nil {
|
||||||
@@ -334,7 +334,7 @@ func (t *table) drawStep(s *drawstate.State, st pathStep, pins *pinSet) node {
|
|||||||
case stepParent:
|
case stepParent:
|
||||||
return t.rows.Parent().Owner()
|
return t.rows.Parent().Owner()
|
||||||
}
|
}
|
||||||
panic(invariant.Broken("drawStep has no case for step kind %d", st.kind))
|
panic(invariant.Broken("followStep has no case for step kind %d", st.kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *table) selector(tail []string) (sel string, rest []string, err error) {
|
func (t *table) selector(tail []string) (sel string, rest []string, err error) {
|
||||||
@@ -348,8 +348,8 @@ func (t *table) selector(tail []string) (sel string, rest []string, err error) {
|
|||||||
return sel, rest, nil
|
return sel, rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// step is what a tail's first segment names in t: a column, or a table linked to it.
|
// columnOrLinked is what a tail's first segment names in t: a column, or a table linked to it.
|
||||||
func (t *table) step(tail []string) (column node, child *table, err error) {
|
func (t *table) columnOrLinked(tail []string) (column node, child *table, err error) {
|
||||||
if len(tail) == 0 {
|
if len(tail) == 0 {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
| 110 | 0.1.0 | | **Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
| 110 | 0.1.0 | | **Refuse an empty segment in a path `Fake`, `FakeRecord` and the CLI take, as a reference does: `sv_SE.person.` reports `path has an empty segment`.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||||
| 79 | 0.1.0 | decision | **Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach.** | 6 | 6 | 7 | 8 | 13 | 12.9 |
|
| 79 | 0.1.0 | decision | **Beside a `--data-path`, load in `New` the user's categories, the shipped ones they override or read, and the shipped ones whose reads reach an overridden one; load the rest on first reach.** | 6 | 6 | 7 | 8 | 13 | 12.9 |
|
||||||
| 131 | 0.1.0 | | **Move `frameStack` into `env.go`, beside the methods that push and search it, and move `nameTargets`, `addressedKeys` and `nameUses` from `resolve.go` into `name.go`.** | 1 | 3 | 1 | 7 | 3.2, 3.1 | 12.8 |
|
| 131 | 0.1.0 | | **Move `frameStack` into `env.go`, beside the methods that push and search it, and move `nameTargets`, `addressedKeys` and `nameUses` from `resolve.go` into `name.go`.** | 1 | 3 | 1 | 7 | 3.2, 3.1 | 12.8 |
|
||||||
| 132 | 0.1.0 | | **Name the path walks and steps by what each does, so `descend`, `probePath`, `pathCheck`, `checkPathReaches`, `compilePath`, `takeStep`, `stepInto` and `table.step` can be told apart by name, `table.drawStep` stops sitting one letter from `drawSteps`, and `pathPos.descended` says what it holds.** | 2 | 3 | 1 | 7 | 3.3 | 11.8 |
|
| 132 | 0.1.0 | | **Name the path walks and steps by what each does, so `descend`, `probePath`, `pathCheck`, `checkPathReaches`, `compilePath`, `takeStep`, `childNamed` and `table.step` can be told apart by name, `table.drawStep` stops sitting one letter from `drawSteps`, and `pathPos.descended` says what it holds.** | 2 | 3 | 1 | 7 | 3.3 | 11.8 |
|
||||||
| 134 | 0.1.0 | | **Hold `nameBinding`'s target in a named field, so `b.target.tail` shows that `resolveTemplates` fills it.** | 1 | 2 | 1 | 6 | 3.3, 3.2 | 11.3 |
|
| 134 | 0.1.0 | | **Hold `nameBinding`'s target in a named field, so `b.target.tail` shows that `resolveTemplates` fills it.** | 1 | 2 | 1 | 6 | 3.3, 3.2 | 11.3 |
|
||||||
| 136 | 0.1.0 | | **Rename `grammar.NameRead` and `grammar.CheckName` for what they read and check, so a name in the engine is only a bound name.** | 1 | 2 | 1 | 6 | 3.3 | 11.3 |
|
| 136 | 0.1.0 | | **Rename `grammar.NameRead` and `grammar.CheckName` for what they read and check, so a name in the engine is only a bound name.** | 1 | 2 | 1 | 6 | 3.3 | 11.3 |
|
||||||
| 135 | 0.1.0 | | **Choose `disagreement`'s message from one table over the two items' kinds.** | 2 | 2 | 1 | 6 | 3.2 | 10.3 |
|
| 135 | 0.1.0 | | **Choose `disagreement`'s message from one table over the two items' kinds.** | 2 | 2 | 1 | 6 | 3.2 | 10.3 |
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
| 108 | 0.2.0 | defect | **Name item 26 in the decision "No builtin reads the clock, so a date is bounded by days, never by an age", as the item that ends its conflict with goal 4.** | 1 | 1 | 1 | 4 | 3.4 | 8.0 |
|
| 108 | 0.2.0 | defect | **Name item 26 in the decision "No builtin reads the clock, so a date is bounded by days, never by an age", as the item that ends its conflict with goal 4.** | 1 | 1 | 1 | 4 | 3.4 | 8.0 |
|
||||||
| 14 | 0.3.0 | | **Spell `misc.creditcard`'s digit runs `{digits(n)}`.** | 2 | 2 | 4 | 6 | 5.7 | 15.1 |
|
| 14 | 0.3.0 | | **Spell `misc.creditcard`'s digit runs `{digits(n)}`.** | 2 | 2 | 4 | 6 | 5.7 | 15.1 |
|
||||||
| 38 | 0.3.0 | defect | **Compare `calcParser.binary`'s operator as a rune.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
| 38 | 0.3.0 | defect | **Compare `calcParser.binary`'s operator as a rune.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||||
| 45 | 0.3.0 | | **Name the node a selector follows in `stepInto`'s refusal.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
| 45 | 0.3.0 | | **Name the node a selector follows in `childNamed`'s refusal.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||||
| 49 | 0.3.0 | | **Report a CRLF rows file holding only its header as having no rows.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
| 49 | 0.3.0 | | **Report a CRLF rows file holding only its header as having no rows.** | 1 | 1 | 3 | 6 | 6 | 13.8 |
|
||||||
| 13 | 0.3.0 | defect | **Place xlsx cells by their `r` reference in `data-import/xlsx.py`.** | 2 | 2 | 3 | 6 | 4 | 13.7 |
|
| 13 | 0.3.0 | defect | **Place xlsx cells by their `r` reference in `data-import/xlsx.py`.** | 2 | 2 | 3 | 6 | 4 | 13.7 |
|
||||||
| 39 | 0.3.0 | | **Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.** | 1 | 2 | 3 | 6 | 6 | 13.5 |
|
| 39 | 0.3.0 | | **Stop the `columnKinds` suggestion naming a field's own kind or a narrower one.** | 1 | 2 | 3 | 6 | 6 | 13.5 |
|
||||||
@@ -299,9 +299,9 @@ The decision "With only the shipped set, a category loads on the first call reac
|
|||||||
|
|
||||||
Both inherited architects, in the panel and in the scoring run, ranked one change first: gather the runtime that keeps a name's pick in one file. `frameStack` sits in `pick.go` while `env.go` pushes and searches it, and the name steps of `resolveTemplates` sit apart from `name.go`. Revise the README's Layout block with it.
|
Both inherited architects, in the panel and in the scoring run, ranked one change first: gather the runtime that keeps a name's pick in one file. `frameStack` sits in `pick.go` while `env.go` pushes and searches it, and the name steps of `resolveTemplates` sit apart from `name.go`. Revise the README's Layout block with it.
|
||||||
|
|
||||||
### 132. Name the path walks and steps by what each does, so `descend`, `probePath`, `pathCheck`, `checkPathReaches`, `compilePath`, `takeStep`, `stepInto` and `table.step` can be told apart by name, `table.drawStep` stops sitting one letter from `drawSteps`, and `pathPos.descended` says what it holds.
|
### 132. Name the path walks and steps by what each does, so `descend`, `probePath`, `pathCheck`, `checkPathReaches`, `compilePath`, `takeStep`, `childNamed` and `table.step` can be told apart by name, `table.drawStep` stops sitting one letter from `drawSteps`, and `pathPos.descended` says what it holds.
|
||||||
|
|
||||||
Six of the nine seats of the 2026-10-07 panel named these names: five entry points walk a path, `descend`, `probePath`, `pathCheck`, `checkPathReaches` and `compilePath`, and five units with near names step through one, `takeStep`, `stepInto`, `table.step`, `table.drawStep` and `drawSteps`; and `pathPos.descended`'s doc, "route's descended flag for it", defines it by its reader.
|
Six of the nine seats of the 2026-10-07 panel named these names: five entry points walk a path, `descend`, `probePath`, `pathCheck`, `checkPathReaches` and `compilePath`, and five units with near names step through one, `takeStep`, `childNamed`, `table.step`, `table.drawStep` and `drawSteps`; and `pathPos.descended`'s doc, "route's descended flag for it", defines it by its reader.
|
||||||
|
|
||||||
### 134. Hold `nameBinding`'s target in a named field, so `b.target.tail` shows that `resolveTemplates` fills it.
|
### 134. Hold `nameBinding`'s target in a named field, so `b.target.tail` shows that `resolveTemplates` fills it.
|
||||||
|
|
||||||
@@ -446,7 +446,7 @@ A `"repeat": 14` over `{digits(1)}` draws every 14-digit run with the odds `{dig
|
|||||||
|
|
||||||
`byte(p.rs[p.pos])` reads U+012B `ī` as `+`, so `{calc(a ī b)}` compiles as `a + b`.
|
`byte(p.rs[p.pos])` reads U+012B `ī` as `+`, so `{calc(a ī b)}` compiles as `a + b`.
|
||||||
|
|
||||||
### 45. Name the node a selector follows in `stepInto`'s refusal.
|
### 45. Name the node a selector follows in `childNamed`'s refusal.
|
||||||
|
|
||||||
`sv_SE.person[1].first` answers `1 is not a table`.
|
`sv_SE.person[1].first` answers `1 is not a table`.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user